diff --git a/LanguageMaster.xml b/LanguageMaster.xml
index a8b7ca047..6632e120c 100644
--- a/LanguageMaster.xml
+++ b/LanguageMaster.xml
@@ -227,7 +227,11 @@ Note: Do check free disk space.
File name ending
TS settings...
Remove line-breaks
+ Delete lines
Try to use source encoding
+ Delete first lines
+ Delete last lines
+ Delete lines containing
"Language" in output file name
Matroska (.mkv) "Language" in output file name: {0}
Two letter language code
@@ -2106,6 +2110,9 @@ can edit in same subtitle file (collaboration)
List view and text box
List view
Text box
+ Use syntax coloring
+ Html color
+ ASSA color
Update
Focus set video position
Toggle dock/undock of video controls
@@ -2117,6 +2124,8 @@ can edit in same subtitle file (collaboration)
Set start, auto duration and go to next
Set end, next start and go to next
Key down=set start, Key up=set end and go to next
+ Set start and set end of previous (minus min gap)
+ Set start and set end of previous and go to next (minus min gap)
Move selected lines 100 ms forward
Move selected lines 100 ms back
Move start {0} ms back
@@ -2151,6 +2160,7 @@ can edit in same subtitle file (collaboration)
Insert new subtitle at key-down, set end time at key-up
Merge dialog (insert dashes)
Go to next line
+ Go to next line and set cursor at end
Go to previous line
Go to current line start
Go to current line end
@@ -2274,6 +2284,7 @@ can edit in same subtitle file (collaboration)
Move last word from first line down (current subtitle)
Selection to lowercase
Selection to uppercase
+ Toggle casing of selection (propercase/uppercase/lowercase)
Selection to Ruby (Japanese)
Toggle auto duration
Auto break text
diff --git a/src/SubtitleEdit.sln b/SubtitleEdit.sln
similarity index 69%
rename from src/SubtitleEdit.sln
rename to SubtitleEdit.sln
index 0fe045426..101c02e65 100644
--- a/src/SubtitleEdit.sln
+++ b/SubtitleEdit.sln
@@ -1,73 +1,68 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29728.190
-MinimumVisualStudioVersion = 14.0.23107.0
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubtitleEdit", "SubtitleEdit.csproj", "{511A5B59-1C35-4719-8536-23B19AF9B21A}"
- ProjectSection(ProjectDependencies) = postProject
- {DBD4656C-5F40-4067-A70B-C4460DE20F77} = {DBD4656C-5F40-4067-A70B-C4460DE20F77}
- EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateAssemblyInfo", "UpdateAssemblyInfo\UpdateAssemblyInfo.csproj", "{DBD4656C-5F40-4067-A70B-C4460DE20F77}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateLanguageFiles", "UpdateLanguageFiles\UpdateLanguageFiles.csproj", "{36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateResourceScript", "UpdateResourceScript\UpdateResourceScript.csproj", "{2CB9698C-F0A8-42FF-8938-DE047292D5FE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibSE", "..\libse\LibSE.csproj", "{3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}"
- ProjectSection(ProjectDependencies) = postProject
- {DBD4656C-5F40-4067-A70B-C4460DE20F77} = {DBD4656C-5F40-4067-A70B-C4460DE20F77}
- {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76} = {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win32Resources", "Win32Resources\Win32Resources.vcxproj", "{905ACC84-B353-4313-BFE5-CCF460B959AF}"
- ProjectSection(ProjectDependencies) = postProject
- {511A5B59-1C35-4719-8536-23B19AF9B21A} = {511A5B59-1C35-4719-8536-23B19AF9B21A}
- {2CB9698C-F0A8-42FF-8938-DE047292D5FE} = {2CB9698C-F0A8-42FF-8938-DE047292D5FE}
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {511A5B59-1C35-4719-8536-23B19AF9B21A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {511A5B59-1C35-4719-8536-23B19AF9B21A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {511A5B59-1C35-4719-8536-23B19AF9B21A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {511A5B59-1C35-4719-8536-23B19AF9B21A}.Release|Any CPU.Build.0 = Release|Any CPU
- {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Release|Any CPU.Build.0 = Release|Any CPU
- {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Release|Any CPU.Build.0 = Release|Any CPU
- {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.Build.0 = Release|Any CPU
- {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Release|Any CPU.Build.0 = Release|Any CPU
- {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3E3CB28F-3A7B-430F-9EB3-0D6C1E53B753}.Release|Any CPU.Build.0 = Release|Any CPU
- {905ACC84-B353-4313-BFE5-CCF460B959AF}.Debug|Any CPU.ActiveCfg = Debug|Win32
- {905ACC84-B353-4313-BFE5-CCF460B959AF}.Release|Any CPU.ActiveCfg = Release|Win32
- {905ACC84-B353-4313-BFE5-CCF460B959AF}.Release|Any CPU.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {E12BDE22-B6A4-4AB6-AF96-F3C0F89C15EC}
- EndGlobalSection
- GlobalSection(TestCaseManagementSettings) = postSolution
- CategoryFile = SubtitleEdit.vsmdi
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29728.190
+MinimumVisualStudioVersion = 14.0.23107.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "src\Test\Test.csproj", "{7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateAssemblyInfo", "src\UpdateAssemblyInfo\UpdateAssemblyInfo.csproj", "{DBD4656C-5F40-4067-A70B-C4460DE20F77}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateLanguageFiles", "src\UpdateLanguageFiles\UpdateLanguageFiles.csproj", "{36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpdateResourceScript", "src\UpdateResourceScript\UpdateResourceScript.csproj", "{2CB9698C-F0A8-42FF-8938-DE047292D5FE}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win32Resources", "src\Win32Resources\Win32Resources.vcxproj", "{905ACC84-B353-4313-BFE5-CCF460B959AF}"
+ ProjectSection(ProjectDependencies) = postProject
+ {511A5B59-1C35-4719-8536-23B19AF9B21A} = {511A5B59-1C35-4719-8536-23B19AF9B21A}
+ {2CB9698C-F0A8-42FF-8938-DE047292D5FE} = {2CB9698C-F0A8-42FF-8938-DE047292D5FE}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B1BB9DD1-0EE8-4D43-AAAB-C39D0CC882A9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibSE", "src\libse\LibSE.csproj", "{D6F64CD3-C3EA-4B36-B575-9B3B8A3CA13F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubtitleEdit", "src\ui\SubtitleEdit.csproj", "{511A5B59-1C35-4719-8536-23B19AF9B21A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BE5B8E8-9469-4C7C-89D7-E8C884DEFC0E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DBD4656C-5F40-4067-A70B-C4460DE20F77}.Release|Any CPU.Build.0 = Release|Any CPU
+ {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {36BCA2A7-EE6B-45FD-AF90-D3F76A84DA76}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2CB9698C-F0A8-42FF-8938-DE047292D5FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {905ACC84-B353-4313-BFE5-CCF460B959AF}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {905ACC84-B353-4313-BFE5-CCF460B959AF}.Release|Any CPU.ActiveCfg = Release|Win32
+ {905ACC84-B353-4313-BFE5-CCF460B959AF}.Release|Any CPU.Build.0 = Release|Win32
+ {D6F64CD3-C3EA-4B36-B575-9B3B8A3CA13F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D6F64CD3-C3EA-4B36-B575-9B3B8A3CA13F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D6F64CD3-C3EA-4B36-B575-9B3B8A3CA13F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D6F64CD3-C3EA-4B36-B575-9B3B8A3CA13F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {511A5B59-1C35-4719-8536-23B19AF9B21A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {511A5B59-1C35-4719-8536-23B19AF9B21A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {511A5B59-1C35-4719-8536-23B19AF9B21A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {511A5B59-1C35-4719-8536-23B19AF9B21A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E12BDE22-B6A4-4AB6-AF96-F3C0F89C15EC}
+ EndGlobalSection
+ GlobalSection(TestCaseManagementSettings) = postSolution
+ CategoryFile = SubtitleEdit.vsmdi
+ EndGlobalSection
+EndGlobal
diff --git a/SubtitleEdit.sln.DotSettings b/SubtitleEdit.sln.DotSettings
new file mode 100644
index 000000000..dd009cf93
--- /dev/null
+++ b/SubtitleEdit.sln.DotSettings
@@ -0,0 +1,24 @@
+
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ True
+ NEVER
+ False
+ False
+ True
+ False
+ SSA
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/build.bat b/build.bat
index 1740cf75f..05e4f9a0f 100644
--- a/build.bat
+++ b/build.bat
@@ -81,7 +81,6 @@ TITLE %BUILDTYPE%ing Subtitle Edit - Release^|Any CPU...
ECHO.
ECHO %BUILDTYPE%ing Subtitle Edit - Release^|Any CPU...
DEL /F /Q SubtitleEdit-*-Setup.exe SubtitleEdit-*.zip 2>NUL
-PUSHD "src"
ECHO.
ECHO Visual Studio installation path: "%VSINSTALLDIR%"
IF EXIST "%VSINSTALLDIR%MSBuild\15.0\Bin\MSBuild.exe" (
@@ -93,16 +92,24 @@ IF EXIST "%VSINSTALLDIR%MSBuild\Current\Bin\MSBuild.exe" (
ECHO Cannot find Visual Studio 2017.
GOTO EndWithError
))
+
"%MSBUILD%" SubtitleEdit.sln /r /t:%BUILDTYPE% /p:Configuration=Release /p:Platform="Any CPU"^
/maxcpucount /consoleloggerparameters:DisableMPLogging;Summary;Verbosity=minimal
IF %ERRORLEVEL% NEQ 0 GOTO EndWithError
IF /I "%BUILDTYPE%" == "Clean" GOTO EndSuccessful
+dir
+PUSHD "src/ui"
+dir
ECHO.
ECHO Merging assemblies with ILRepack...
FOR /D %%A IN (packages\ILRepack.*) DO (SET "ILREPACKDIR=%%A")
ECHO.
+dir
+echo ILREPACKDIR
+echo %ILREPACKDIR%
+echo ILREPACKDIR done
"%ILREPACKDIR%\tools\ILRepack.exe" /parallel /internalize /targetplatform:v4 /out:"bin\Release\SubtitleEdit.exe" "bin\Release\SubtitleEdit.exe"^
"bin\Release\libse.dll" "bin\Release\zlib.net.dll" "bin\Release\NHunspell.dll" "bin\Release\UtfUnknown.dll" "DLLs\Interop.QuartzTypeLib.dll"
IF %ERRORLEVEL% NEQ 0 GOTO EndWithError
@@ -166,7 +173,7 @@ EXIT /B
TITLE Creating ZIP archive with 7-Zip...
ECHO.
ECHO Creating ZIP archive with 7-Zip...
-PUSHD "src\bin\Release"
+PUSHD "src\ui\bin\Release"
IF EXIST "temp_zip" RD /S /Q "temp_zip"
IF NOT EXIST "temp_zip" MD "temp_zip"
IF NOT EXIST "temp_zip\Languages" MD "temp_zip\Languages"
@@ -174,14 +181,14 @@ IF NOT EXIST "temp_zip\Dictionaries" MD "temp_zip\Dictionaries"
IF NOT EXIST "temp_zip\Ocr" MD "temp_zip\Ocr"
ECHO.
-COPY /Y /V "..\..\..\LICENSE.txt" "temp_zip\"
-COPY /Y /V "..\..\..\Changelog.txt" "temp_zip\"
+COPY /Y /V "..\..\..\..\LICENSE.txt" "temp_zip\"
+COPY /Y /V "..\..\..\..\Changelog.txt" "temp_zip\"
COPY /Y /V "Hunspellx86.dll" "temp_zip\"
COPY /Y /V "Hunspellx64.dll" "temp_zip\"
COPY /Y /V "SubtitleEdit.exe" "temp_zip\"
COPY /Y /V "Languages\*.xml" "temp_zip\Languages\"
-COPY /Y /V "..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
-COPY /Y /V "..\..\..\Ocr\*.*" "temp_zip\Ocr\"
+COPY /Y /V "..\..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
+COPY /Y /V "..\..\..\..\Ocr\*.*" "temp_zip\Ocr\"
PUSHD "temp_zip"
START "" /B /WAIT "%SEVENZIP%" a -tzip -mx=9 "SubtitleEdit-%VERSION%.zip" * >NUL
@@ -189,7 +196,7 @@ IF %ERRORLEVEL% NEQ 0 GOTO EndWithError
ECHO.
ECHO ZIP archive created successfully!
-MOVE /Y "SubtitleEdit-%VERSION%.zip" "..\..\..\.." >NUL
+MOVE /Y "SubtitleEdit-%VERSION%.zip" "..\..\..\..\.." >NUL
POPD
IF EXIST "temp_zip" RD /S /Q "temp_zip"
POPD
@@ -198,7 +205,7 @@ EXIT /B
:SubGetVersion
-FOR /F delims^=^"^ tokens^=2 %%A IN ('FINDSTR /R /C:"AssemblyVersion" "src\Properties\AssemblyInfo.cs.template"') DO (
+FOR /F delims^=^"^ tokens^=2 %%A IN ('FINDSTR /R /C:"AssemblyVersion" "src\ui\Properties\AssemblyInfo.cs.template"') DO (
REM 3.4.1.[REVNO]
SET "VERSION=%%A"
)
diff --git a/build_helpers.bat b/build_helpers.bat
index 12c9a4651..06ed164cb 100644
--- a/build_helpers.bat
+++ b/build_helpers.bat
@@ -20,7 +20,7 @@ IF NOT EXIST "%ToolPath%" (
GOTO END
)
-"%ToolPath%" "src\Win32Resources\Resources.rc.template" "src\bin\%ConfigurationName%\SubtitleEdit.exe"
+"%ToolPath%" "src\Win32Resources\Resources.rc.template" "src\ui\bin\%ConfigurationName%\SubtitleEdit.exe"
IF %ERRORLEVEL% NEQ 0 (
ECHO ERROR: Something went wrong when generating the resource script...
@@ -35,7 +35,7 @@ IF NOT EXIST "%ToolPath%" (
GOTO END
)
-"%ToolPath%" "LanguageMaster.xml" "libse\LanguageDeserializer.cs"
+"%ToolPath%" "LanguageMaster.xml" "src\libse\LanguageDeserializer.cs"
IF %ERRORLEVEL% NEQ 0 (
ECHO ERROR: Something went wrong when generating the language files...
@@ -50,7 +50,7 @@ IF NOT EXIST "%ToolPath%" (
GOTO END
)
-"%ToolPath%" "src\Properties\AssemblyInfo.cs.template" "libse\Properties\AssemblyInfo.cs.template"
+"%ToolPath%" "src\ui\Properties\AssemblyInfo.cs.template" "src\libse\Properties\AssemblyInfo.cs.template"
IF %ERRORLEVEL% NEQ 0 (
ECHO ERROR: Something went wrong when generating the revision number...
diff --git a/coverity.bat b/coverity.bat
deleted file mode 100644
index dfddfa2fc..000000000
--- a/coverity.bat
+++ /dev/null
@@ -1,73 +0,0 @@
-@ECHO OFF
-
-SETLOCAL
-
-PUSHD %~dp0
-
-IF NOT DEFINED COVDIR SET "COVDIR=H:\progs\thirdparty\cov-analysis-win64"
-IF DEFINED COVDIR IF NOT EXIST "%COVDIR%" (
- ECHO.
- ECHO ERROR: Coverity not found in "%COVDIR%"
- GOTO End
-)
-
-
-CALL "%VS140COMNTOOLS%\vsvars32.bat"
-IF %ERRORLEVEL% NEQ 0 (
- ECHO vsvars32.bat call failed.
- GOTO End
-)
-
-
-:Cleanup
-IF EXIST "cov-int" RD /q /s "cov-int"
-IF EXIST "SubtitleEdit.lzma" DEL "SubtitleEdit.lzma"
-IF EXIST "SubtitleEdit.tar" DEL "SubtitleEdit.tar"
-IF EXIST "SubtitleEdit.tgz" DEL "SubtitleEdit.tgz"
-
-
-:Main
-SET MSBUILD_SWITCHES=/nologo /t:Rebuild /p:Configuration=Release /p:Platform="Any CPU"^
- /maxcpucount /consoleloggerparameters:DisableMPLogging;Summary;Verbosity=minimal
-
-"%COVDIR%\bin\cov-build.exe" --dir cov-int MSBuild.exe src\SubtitleEdit.sln %MSBUILD_SWITCHES%
-
-
-:tar
-tar --version 1>&2 2>NUL || (ECHO. & ECHO ERROR: tar not found & GOTO SevenZip)
-tar caf "SubtitleEdit.lzma" "cov-int"
-GOTO End
-
-
-:SevenZip
-CALL :SubDetectSevenzipPath
-
-rem Coverity is totally bogus with lzma...
-rem And since I cannot replicate the arguments with 7-Zip, just use tar/gzip.
-IF EXIST "%SEVENZIP%" (
- "%SEVENZIP%" a -ttar "SubtitleEdit.tar" "cov-int"
- "%SEVENZIP%" a -tgzip "SubtitleEdit.tgz" "SubtitleEdit.tar"
- IF EXIST "SubtitleEdit.tar" DEL "SubtitleEdit.tar"
- GOTO End
-)
-
-
-:SubDetectSevenzipPath
-FOR %%G IN (7z.exe) DO (SET "SEVENZIP_PATH=%%~$PATH:G")
-IF EXIST "%SEVENZIP_PATH%" (SET "SEVENZIP=%SEVENZIP_PATH%" & EXIT /B)
-
-FOR %%G IN (7za.exe) DO (SET "SEVENZIP_PATH=%%~$PATH:G")
-IF EXIST "%SEVENZIP_PATH%" (SET "SEVENZIP=%SEVENZIP_PATH%" & EXIT /B)
-
-FOR /F "tokens=2*" %%A IN (
- 'REG QUERY "HKLM\SOFTWARE\7-Zip" /v "Path" 2^>NUL ^| FIND "REG_SZ" ^|^|
- REG QUERY "HKLM\SOFTWARE\Wow6432Node\7-Zip" /v "Path" 2^>NUL ^| FIND "REG_SZ"') DO SET "SEVENZIP=%%B\7z.exe"
-EXIT /B
-
-
-:End
-POPD
-ECHO. & ECHO Press any key to close this window...
-PAUSE >NUL
-ENDLOCAL
-EXIT /B
diff --git a/installer/Subtitle_Edit_installer.iss b/installer/Subtitle_Edit_installer.iss
index 80312784e..18cb1dcf0 100644
--- a/installer/Subtitle_Edit_installer.iss
+++ b/installer/Subtitle_Edit_installer.iss
@@ -44,7 +44,7 @@
#define VerBuild
#define VerRevision
-#define bindir "..\src\bin\Release"
+#define bindir "..\src\ui\bin\Release"
#ifnexist bindir + "\SubtitleEdit.exe"
#error Compile Subtitle Edit first
@@ -92,7 +92,7 @@ VersionInfoVersion={#app_ver_full}
MinVersion=5.6
LicenseFile=..\LICENSE.txt
InfoAfterFile=..\Changelog.txt
-SetupIconFile=..\src\Icons\SE.ico
+SetupIconFile=..\src\ui\Icons\SE.ico
WizardImageFile=Icons\WizardImageFile.bmp
WizardSmallImageFile=Icons\WizardSmallImageFile.bmp
OutputDir=.
diff --git a/src/packages.config b/packages.config
similarity index 100%
rename from src/packages.config
rename to packages.config
diff --git a/run_tabspace.bat b/run_tabspace.bat
deleted file mode 100644
index a39cfc067..000000000
--- a/run_tabspace.bat
+++ /dev/null
@@ -1,10 +0,0 @@
-@ECHO OFF
-SETLOCAL
-
-PUSHD %~dp0
-
-tabspace /ext:bat;c;cc;cpp;cs;cxx;h;hpp;hxx;iss;xml /exclude:*\*.designer.cs
-
-POPD
-ENDLOCAL
-EXIT /B
diff --git a/src/Forms/ChooseAudioTrack.resx b/src/Forms/ChooseAudioTrack.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/ChooseAudioTrack.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/ChooseStyle.resx b/src/Forms/ChooseStyle.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/ChooseStyle.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/ColumnPaste.resx b/src/Forms/ColumnPaste.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/ColumnPaste.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/DoNotBreakAfterListEdit.resx b/src/Forms/DoNotBreakAfterListEdit.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/DoNotBreakAfterListEdit.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/DurationsBridgeGaps.resx b/src/Forms/DurationsBridgeGaps.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/DurationsBridgeGaps.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/DvdSubRipChooseLanguage.resx b/src/Forms/DvdSubRipChooseLanguage.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/DvdSubRipChooseLanguage.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/FcpProperties.resx b/src/Forms/FcpProperties.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/FcpProperties.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/GoogleOrMicrosoftTranslate.resx b/src/Forms/GoogleOrMicrosoftTranslate.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/GoogleOrMicrosoftTranslate.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/Interjections.resx b/src/Forms/Interjections.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/Interjections.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Forms/MeasurementConverter.resx b/src/Forms/MeasurementConverter.resx
deleted file mode 100644
index 29dcb1b3a..000000000
--- a/src/Forms/MeasurementConverter.resx
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Test/Test.csproj b/src/Test/Test.csproj
index 2eccdc8a6..2a08429ec 100644
--- a/src/Test/Test.csproj
+++ b/src/Test/Test.csproj
@@ -92,16 +92,6 @@
-
-
- {3e3cb28f-3a7b-430f-9eb3-0d6c1e53b753}
- LibSE
-
-
- {511A5B59-1C35-4719-8536-23B19AF9B21A}
- SubtitleEdit
-
-
@@ -164,7 +154,16 @@
-
+
+
+ {d6f64cd3-c3ea-4b36-b575-9b3b8a3ca13f}
+ LibSE
+
+
+ {511a5b59-1c35-4719-8536-23b19af9b21a}
+ SubtitleEdit
+
+
> 072058
- //Meine Mutter und meine Schwester,
-
- //-->> 072169
-
- //-->> 072172
- //die in Zürich lebt, und ich,
-
- //-->> 072247
- const string paragraphWriteFormat = "-->> {0}{3}{2}{3}-->> {1}{3}{3}";
-
- var sb = new StringBuilder();
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- string text = Utilities.RemoveSsaTags(p.Text);
- int noOfLines = Utilities.GetNumberOfLines(text);
- if (noOfLines > 2)
- {
- text = Utilities.AutoBreakLine(text);
- }
- else if (noOfLines == 1)
- {
- text += Environment.NewLine;
- }
-
- sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine));
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- var paragraph = new Paragraph();
- var expecting = ExpectingLine.TimeStart;
- _errorCount = 0;
-
- subtitle.Paragraphs.Clear();
- foreach (string line in lines)
- {
- if (line.StartsWith("-->> ", StringComparison.Ordinal))
- {
- string timePart = line.Substring(4).Trim();
- if (timePart.Length > 0)
- {
- try
- {
- var tc = DecodeTimeCode(timePart);
- if (expecting == ExpectingLine.TimeStart)
- {
- paragraph = new Paragraph { StartTime = tc };
- expecting = ExpectingLine.Text;
- }
- else if (expecting == ExpectingLine.TimeEndOrText)
- {
- paragraph.EndTime = tc;
- subtitle.Paragraphs.Add(paragraph);
- paragraph = new Paragraph();
- expecting = ExpectingLine.TimeStart;
- }
- }
- catch
- {
- _errorCount++;
- expecting = ExpectingLine.TimeStart;
- }
- }
- }
- else
- {
- if (expecting == ExpectingLine.Text || expecting == ExpectingLine.TimeEndOrText)
- {
- if (line.Length > 0)
- {
- string text = line.Replace("|", Environment.NewLine);
- if (string.IsNullOrEmpty(paragraph.Text))
- {
- paragraph.Text = text.Trim();
- }
- else
- {
- paragraph.Text += Environment.NewLine + text;
- }
-
- if (paragraph.Text.Length > 2000)
- {
- _errorCount += 100;
- return;
- }
- }
- expecting = ExpectingLine.TimeEndOrText;
- }
- else if (expecting == ExpectingLine.TimeStart && !string.IsNullOrWhiteSpace(line))
- {
- int ms = (int)paragraph.EndTime.TotalMilliseconds;
- paragraph = new Paragraph { StartTime = { TotalMilliseconds = ms }, Text = line.Trim() };
- expecting = ExpectingLine.TimeEndOrText;
- }
- }
- }
- subtitle.Renumber();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- int frames = MillisecondsToFrames(time.TotalMilliseconds) + 1;
- return frames.ToString(CultureInfo.InvariantCulture);
- }
-
- private static TimeCode DecodeTimeCode(string timePart)
- {
- int milliseconds = (int)Math.Round(TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate * int.Parse(timePart));
- return new TimeCode(milliseconds);
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class AQTitle : SubtitleFormat
+ {
+ private enum ExpectingLine
+ {
+ TimeStart,
+ Text,
+ TimeEndOrText,
+ }
+
+ public override string Extension => ".aqt";
+
+ public override string Name => "AQTitle";
+
+ public override bool IsTimeBased => false;
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ //-->> 072058
+ //Meine Mutter und meine Schwester,
+
+ //-->> 072169
+
+ //-->> 072172
+ //die in Zürich lebt, und ich,
+
+ //-->> 072247
+ const string paragraphWriteFormat = "-->> {0}{3}{2}{3}-->> {1}{3}{3}";
+
+ var sb = new StringBuilder();
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ string text = Utilities.RemoveSsaTags(p.Text);
+ int noOfLines = Utilities.GetNumberOfLines(text);
+ if (noOfLines > 2)
+ {
+ text = Utilities.AutoBreakLine(text);
+ }
+ else if (noOfLines == 1)
+ {
+ text += Environment.NewLine;
+ }
+
+ sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ var paragraph = new Paragraph();
+ var expecting = ExpectingLine.TimeStart;
+ _errorCount = 0;
+
+ subtitle.Paragraphs.Clear();
+ foreach (string line in lines)
+ {
+ if (line.StartsWith("-->> ", StringComparison.Ordinal))
+ {
+ string timePart = line.Substring(4).Trim();
+ if (timePart.Length > 0)
+ {
+ try
+ {
+ var tc = DecodeTimeCode(timePart);
+ if (expecting == ExpectingLine.TimeStart)
+ {
+ paragraph = new Paragraph { StartTime = tc };
+ expecting = ExpectingLine.Text;
+ }
+ else if (expecting == ExpectingLine.TimeEndOrText)
+ {
+ paragraph.EndTime = tc;
+ subtitle.Paragraphs.Add(paragraph);
+ paragraph = new Paragraph();
+ expecting = ExpectingLine.TimeStart;
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ expecting = ExpectingLine.TimeStart;
+ }
+ }
+ }
+ else
+ {
+ if (expecting == ExpectingLine.Text || expecting == ExpectingLine.TimeEndOrText)
+ {
+ if (line.Length > 0)
+ {
+ string text = line.Replace("|", Environment.NewLine);
+ if (string.IsNullOrEmpty(paragraph.Text))
+ {
+ paragraph.Text = text.Trim();
+ }
+ else
+ {
+ paragraph.Text += Environment.NewLine + text;
+ }
+
+ if (paragraph.Text.Length > 2000)
+ {
+ _errorCount += 100;
+ return;
+ }
+ }
+ expecting = ExpectingLine.TimeEndOrText;
+ }
+ else if (expecting == ExpectingLine.TimeStart && !string.IsNullOrWhiteSpace(line))
+ {
+ int ms = (int)paragraph.EndTime.TotalMilliseconds;
+ paragraph = new Paragraph { StartTime = { TotalMilliseconds = ms }, Text = line.Trim() };
+ expecting = ExpectingLine.TimeEndOrText;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ int frames = MillisecondsToFrames(time.TotalMilliseconds) + 1;
+ return frames.ToString(CultureInfo.InvariantCulture);
+ }
+
+ private static TimeCode DecodeTimeCode(string timePart)
+ {
+ int milliseconds = (int)Math.Round(TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate * int.Parse(timePart));
+ return new TimeCode(milliseconds);
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/AbcIViewer.cs b/src/libse/SubtitleFormats/AbcIViewer.cs
similarity index 97%
rename from libse/SubtitleFormats/AbcIViewer.cs
rename to src/libse/SubtitleFormats/AbcIViewer.cs
index ad8192616..230b47c38 100644
--- a/libse/SubtitleFormats/AbcIViewer.cs
+++ b/src/libse/SubtitleFormats/AbcIViewer.cs
@@ -1,120 +1,120 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class AbcIViewer : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "ABC iView";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(xmlStructure);
- XmlNode reel = xml.DocumentElement.SelectSingleNode("reel");
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("title");
-
- XmlAttribute start = xml.CreateAttribute("start");
- start.InnerText = ToTimeCode(p.StartTime.TotalMilliseconds);
- paragraph.Attributes.Append(start);
-
- XmlAttribute end = xml.CreateAttribute("end");
- end.InnerText = ToTimeCode(p.EndTime.TotalMilliseconds);
- paragraph.Attributes.Append(end);
-
- paragraph.InnerText = HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "|"), true);
-
- reel.AppendChild(paragraph);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- private static string ToTimeCode(double totalMilliseconds)
- {
- var ts = TimeSpan.FromMilliseconds(totalMilliseconds);
- return $"{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}:{ts.Milliseconds:00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- bool allTwoCifferMs = true;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string xmlString = sb.ToString();
- if (!xmlString.Contains(" ".xml";
+
+ public override string Name => "ABC iView";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(xmlStructure);
+ XmlNode reel = xml.DocumentElement.SelectSingleNode("reel");
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("title");
+
+ XmlAttribute start = xml.CreateAttribute("start");
+ start.InnerText = ToTimeCode(p.StartTime.TotalMilliseconds);
+ paragraph.Attributes.Append(start);
+
+ XmlAttribute end = xml.CreateAttribute("end");
+ end.InnerText = ToTimeCode(p.EndTime.TotalMilliseconds);
+ paragraph.Attributes.Append(end);
+
+ paragraph.InnerText = HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "|"), true);
+
+ reel.AppendChild(paragraph);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ private static string ToTimeCode(double totalMilliseconds)
+ {
+ var ts = TimeSpan.FromMilliseconds(totalMilliseconds);
+ return $"{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}:{ts.Milliseconds:00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ bool allTwoCifferMs = true;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string xmlString = sb.ToString();
+ if (!xmlString.Contains(" ".xml";
-
- public override string Name => "Adobe After Effects ft-MarkerExporter";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure = @"
-
-
- 1
-
-
-
-
-
-".Replace("'", "\"");
-
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(xmlStructure);
- const string innerXml = "", @"{\i}");
- text = text.Replace("", @"{\u1}");
- text = text.Replace("", @"{\u0}");
- text = text.Replace("", @"{\u}");
- text = text.Replace("", @"{\b1}");
- text = text.Replace("", @"{\b0}");
- text = text.Replace("", @"{\b}");
- int count = 0;
- while (text.Contains("', start);
- if (end > 0)
- {
- string fontTag = text.Substring(start + 5, end - (start + 4));
- text = text.Remove(start, end - start + 1);
- int indexOfEndFont = text.IndexOf("", start, StringComparison.Ordinal);
- if (indexOfEndFont > 0)
- {
- text = text.Remove(indexOfEndFont, 7);
- if (indexOfEndFont < text.Length)
- {
- if (fontTag.Contains(" size="))
- {
- text = text.Insert(indexOfEndFont, "{\\fs}");
- }
- if (fontTag.Contains(" face="))
- {
- text = text.Insert(indexOfEndFont, "{\\fn}");
- }
- if (fontTag.Contains(" color="))
- {
- text = text.Insert(indexOfEndFont, "{\\c}");
- }
- }
- }
-
- fontTag = FormatTag(ref text, start, fontTag, "face=\"", "fn", "}");
- fontTag = FormatTag(ref text, start, fontTag, "face='", "fn", "}");
- fontTag = FormatTag(ref text, start, fontTag, "face=", "fn", "}");
-
- fontTag = FormatTag(ref text, start, fontTag, "size=\"", "fs", "}");
- fontTag = FormatTag(ref text, start, fontTag, "size='", "fs", "}");
- fontTag = FormatTag(ref text, start, fontTag, "size=", "fs", "}");
-
- fontTag = FormatTag(ref text, start, fontTag, "color=\"", "c&H", "&}");
- fontTag = FormatTag(ref text, start, fontTag, "color='", "c&H", "&}");
- FormatTag(ref text, start, fontTag, "color=", "c&H", "&}");
- }
- count++;
- }
- text = text.Replace("{\\c}", "@___@@").Replace("}{", string.Empty).Replace("@___@@", "{\\c}").Replace("{\\c}{\\c&", "{\\c&");
- while (text.EndsWith("{\\c}", StringComparison.Ordinal))
- {
- text = text.Remove(text.Length - 4);
- }
- while (text.Contains("\\fs\\fs", StringComparison.Ordinal))
- {
- text = text.Replace("\\fs\\fs", "\\fs");
- }
- while (text.Contains("\\fn\\fn", StringComparison.Ordinal))
- {
- text = text.Replace("\\fn\\fn", "\\fn");
- }
- while (text.Contains("\\c\\c&H", StringComparison.Ordinal))
- {
- text = text.Replace("\\c\\c&H", "\\c&H");
- }
- return text;
- }
-
- private static string FormatTag(ref string text, int start, string fontTag, string tag, string ssaTagName, string endSsaTag)
- {
- if (fontTag.Contains(tag))
- {
- int fontStart = fontTag.IndexOf(tag, StringComparison.Ordinal);
-
- int fontEnd = fontTag.IndexOfAny(new[] { '"', '\'' }, fontStart + tag.Length);
- if (fontEnd < 0)
- {
- fontEnd = fontTag.IndexOfAny(new[] { ' ', '>' }, fontStart + tag.Length);
- }
-
- if (fontEnd > 0)
- {
- string subTag = fontTag.Substring(fontStart + tag.Length, fontEnd - (fontStart + tag.Length));
- if (tag.Contains("color"))
- {
- Color c;
- try
- {
- c = ColorTranslator.FromHtml(subTag);
- }
- catch
- {
- c = Color.White;
- }
- subTag = (c.B.ToString("X2") + c.G.ToString("X2") + c.R.ToString("X2")).ToLowerInvariant(); // use bbggrr
- }
- fontTag = fontTag.Remove(fontStart, fontEnd - fontStart + 1);
- if (start < text.Length)
- {
- text = text.Insert(start, @"{\" + ssaTagName + subTag + endSsaTag);
- }
- }
- }
- return fontTag;
- }
-
- public static string GetFormattedText(string input)
- {
- var text = input.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine);
-
- var tooComplex = ContainsUnsupportedTags(text);
-
- if (!tooComplex)
- {
- for (int i = 0; i < 10; i++) // just look ten times...
- {
- bool italic;
- if (text.Contains(@"{\fn"))
- {
- int start = text.IndexOf(@"{\fn", StringComparison.Ordinal);
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal))
- {
- string fontName = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontName, ref extraTags, out var unknownTags, out italic);
- text = text.Remove(start, end - start + 1);
- if (italic)
- {
- text = text.Insert(start, "" + unknownTags + "");
- }
- else
- {
- text = text.Insert(start, "" + unknownTags);
- }
-
- int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal);
- if (indexOfEndTag > 0)
- {
- text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "");
- }
- else
- {
- int indexOfNextTag1 = text.IndexOf("{\\fn", start, StringComparison.Ordinal);
- int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
- if (indexOfNextTag1 > 0)
- {
- text = text.Insert(indexOfNextTag1, "");
- }
- else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
- {
- text = text.Insert(indexOfNextTag2, "");
- }
- else
- {
- text += "";
- }
- }
- }
- }
-
- if (text.Contains(@"{\fs"))
- {
- int start = text.IndexOf(@"{\fs", StringComparison.Ordinal);
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal))
- {
- string fontSize = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontSize, ref extraTags, out var unknownTags, out italic);
- if (float.TryParse(fontSize, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _))
- {
- text = text.Remove(start, end - start + 1);
- if (italic)
- {
- text = text.Insert(start, "" + unknownTags + "");
- }
- else
- {
- text = text.Insert(start, "" + unknownTags);
- }
-
- int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal);
- if (indexOfEndTag > 0)
- {
- text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "");
- }
- else
- {
- int indexOfNextTag1 = text.IndexOf("{\\fs", start, StringComparison.Ordinal);
- int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
- if (indexOfNextTag1 > 0)
- {
- text = text.Insert(indexOfNextTag1, "");
- }
- else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
- {
- text = text.Insert(indexOfNextTag2, "");
- }
- else
- {
- text += "";
- }
- }
- }
- }
- }
-
- if (text.Contains(@"{\c"))
- {
- int start = text.IndexOf(@"{\c", StringComparison.Ordinal);
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal) && !text.Substring(start).StartsWith("{\\clip", StringComparison.Ordinal))
- {
- string color = text.Substring(start + 4, end - (start + 4));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out var unknownTags, out italic);
-
- color = color.RemoveChar('&').TrimStart('H');
- color = color.PadLeft(6, '0');
-
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLowerInvariant();
-
- text = text.Remove(start, end - start + 1);
- if (italic)
- {
- text = text.Insert(start, "" + unknownTags + "");
- }
- else
- {
- text = text.Insert(start, "" + unknownTags);
- }
-
- int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
- int indexOfNextColorTag = text.IndexOf("{\\c&", start, StringComparison.Ordinal);
- if (indexOfNextColorTag > 0 && (indexOfNextColorTag < indexOfEndTag || indexOfEndTag == -1))
- {
- text = text.Insert(indexOfNextColorTag, "");
- }
- else if (indexOfEndTag > 0)
- {
- text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "");
- }
- else
- {
- text += "";
- }
- }
- }
-
- if (text.Contains(@"{\1c")) // "1" specifices primary color
- {
- int start = text.IndexOf(@"{\1c", StringComparison.Ordinal);
- int end = text.IndexOf('}', start);
- if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal))
- {
- string color = text.Substring(start + 5, end - (start + 5));
- string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out var unknownTags, out italic);
-
- color = color.RemoveChar('&').TrimStart('H');
- color = color.PadLeft(6, '0');
-
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLowerInvariant();
-
- text = text.Remove(start, end - start + 1);
- if (italic)
- {
- text = text.Insert(start, "" + unknownTags + "");
- }
- else
- {
- text = text.Insert(start, "" + unknownTags);
- }
-
- text += "";
- }
- }
- }
- }
-
- text = text.Replace(@"{\i1}", "");
- text = text.Replace(@"{\i0}", "");
- text = text.Replace(@"{\i}", "");
- if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
- {
- text += "";
- }
-
- text = text.Replace(@"{\u1}", "");
- text = text.Replace(@"{\u0}", "");
- text = text.Replace(@"{\u}", "");
- if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
- {
- text += "";
- }
-
- text = text.Replace(@"{\b1}", "");
- text = text.Replace(@"{\b0}", "");
- text = text.Replace(@"{\b}", "");
- if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
- {
- text += "";
- }
-
- return text;
- }
-
- private static bool ContainsUnsupportedTags(string text)
- {
- if (string.IsNullOrEmpty(text) || !text.Contains("{\\", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- var unsupportedTags = new List
- {
- "\\alpha",
- "\\be0",
- "\\be1",
- "\\bord",
- "\\blur",
- "\\clip",
- "\\fad",
- "\\fa",
- "\\fade",
- "\\fscx",
- "\\fscy",
- "\\fr",
- "\\iclip",
- "\\k",
- "\\K",
- "\\kf",
- "\\ko",
- "\\move",
- "\\org",
- "\\p",
- "\\pos",
- "\\s0",
- "\\s1",
- "\\t(",
- "\\xbord",
- "\\ybord",
- "\\xshad",
- "\\yshad"
- };
-
- foreach (var unsupportedTag in unsupportedTags)
- {
- if (text.Contains(unsupportedTag, StringComparison.Ordinal))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out string unknownTags, out bool italic)
- {
- italic = false;
- unknownTags = string.Empty;
- int indexOfSPlit = tagName.IndexOf('\\');
- if (indexOfSPlit > 0)
- {
- string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
- tagName = tagName.Remove(indexOfSPlit);
-
- for (int i = 0; i < 10; i++)
- {
- if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf('\\');
- string fontSize = rest;
- if (indexOfSPlit > 0)
- {
- fontSize = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- extraTags += " size=\"" + fontSize.Substring(2) + "\"";
- }
- else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf('\\');
- string fontName = rest;
- if (indexOfSPlit > 0)
- {
- fontName = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- extraTags += " face=\"" + fontName.Substring(2) + "\"";
- }
- else if (rest.StartsWith('c') && rest.Length > 2)
- {
- indexOfSPlit = rest.IndexOf('\\');
- string fontColor = rest;
- if (indexOfSPlit > 0)
- {
- fontColor = rest.Substring(0, indexOfSPlit);
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
-
- string color = fontColor.Substring(2);
- color = color.RemoveChar('&').TrimStart('H');
- color = color.PadLeft(6, '0');
- // switch to rrggbb from bbggrr
- color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
- color = color.ToLowerInvariant();
-
- extraTags += " color=\"" + color + "\"";
- }
- else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1)
- {
- indexOfSPlit = rest.IndexOf('\\');
- italic = true;
- if (indexOfSPlit > 0)
- {
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else
- {
- rest = string.Empty;
- }
- }
- else if (rest.Length > 0 && rest.Contains("\\"))
- {
- indexOfSPlit = rest.IndexOf('\\');
- var unknowntag = rest.Substring(0, indexOfSPlit);
- unknownTags += "\\" + unknowntag;
- rest = rest.Substring(indexOfSPlit).TrimStart('\\');
- }
- else if (!string.IsNullOrEmpty(rest))
- {
- unknownTags += "\\" + rest;
- rest = string.Empty;
- }
- }
- }
- if (!string.IsNullOrEmpty(unknownTags))
- {
- unknownTags = "{" + unknownTags + "}";
- }
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- Errors = null;
- bool eventsStarted = false;
- bool fontsStarted = false;
- bool graphicsStarted = false;
- subtitle.Paragraphs.Clear();
-
- // Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text
- int indexLayer = 0;
- int indexStart = 1;
- int indexEnd = 2;
- int indexStyle = 3;
- int indexActor = -1; // convert "Actor" to "Nam" (if no "Name")
- int indexName = 4;
- int indexMarginL = 5;
- int indexMarginR = 6;
- int indexMarginV = 7;
- int indexEffect = 8;
- int indexText = 9;
- var errors = new StringBuilder();
- int lineNumber = 0;
-
- var header = new StringBuilder();
- var footer = new StringBuilder();
- foreach (string line in lines)
- {
- lineNumber++;
- if (!eventsStarted && !fontsStarted && !graphicsStarted &&
- !line.Trim().Equals("[fonts]", StringComparison.InvariantCultureIgnoreCase) &&
- !line.Trim().Equals("[graphics]", StringComparison.InvariantCultureIgnoreCase))
- {
- header.AppendLine(line);
- }
-
- if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith(';'))
- {
- // skip empty and comment lines
- }
- else if (line.TrimStart().StartsWith("dialog:", StringComparison.OrdinalIgnoreCase) || line.TrimStart().StartsWith("dialogue:", StringComparison.OrdinalIgnoreCase)) // fix faulty font tags...
- {
- eventsStarted = true;
- fontsStarted = false;
- graphicsStarted = false;
- }
-
- if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase))
- {
- if (header.ToString().IndexOf(Environment.NewLine + "[events]", StringComparison.OrdinalIgnoreCase) < 0)
- {
- var h = header.ToString().TrimEnd();
- header.Clear();
- header.AppendLine(h);
- header.AppendLine();
- header.AppendLine("[Events]");
- }
- eventsStarted = true;
- fontsStarted = false;
- graphicsStarted = false;
- }
- else if (line.Trim().Equals("[fonts]", StringComparison.OrdinalIgnoreCase))
- {
- eventsStarted = false;
- fontsStarted = true;
- graphicsStarted = false;
- footer.AppendLine();
- footer.AppendLine("[Fonts]");
- }
- else if (line.Trim().Equals("[graphics]", StringComparison.OrdinalIgnoreCase))
- {
- eventsStarted = false;
- fontsStarted = false;
- graphicsStarted = true;
- footer.AppendLine();
- footer.AppendLine("[Graphics]");
- }
- else if (line.Trim().Equals("[Aegisub Extradata]", StringComparison.OrdinalIgnoreCase))
- {
- eventsStarted = false;
- fontsStarted = false;
- graphicsStarted = true;
- footer.AppendLine();
- footer.AppendLine("[Aegisub Extradata]");
- }
- else if (fontsStarted)
- {
- footer.AppendLine(line);
- }
- else if (graphicsStarted)
- {
- footer.AppendLine(line);
- }
- else if (eventsStarted)
- {
- string s = line.Trim().ToLowerInvariant();
- if (line.Length > 10 && s.StartsWith("format:", StringComparison.Ordinal))
- {
- indexLayer = -1;
- indexStart = -1;
- indexEnd = -1;
- indexStyle = -1;
- indexActor = -1;
- indexName = -1;
- indexMarginL = -1;
- indexMarginR = -1;
- indexMarginV = -1;
- indexEffect = -1;
- indexText = -1;
-
- var format = s.Substring(8).Split(',');
- for (int i = 0; i < format.Length; i++)
- {
- var formatTrimmed = format[i].Trim();
- if (formatTrimmed.Equals("start", StringComparison.Ordinal))
- {
- indexStart = i;
- }
- else if (formatTrimmed.Equals("end", StringComparison.Ordinal))
- {
- indexEnd = i;
- }
- else if (formatTrimmed.Equals("text", StringComparison.Ordinal))
- {
- indexText = i;
- }
- else if (formatTrimmed.Equals("style", StringComparison.Ordinal))
- {
- indexStyle = i;
- }
- else if (formatTrimmed.Equals("actor", StringComparison.Ordinal))
- {
- indexActor = i;
- }
- else if (formatTrimmed.Equals("name", StringComparison.Ordinal))
- {
- indexName = i;
- }
- else if (formatTrimmed.Equals("marginl", StringComparison.Ordinal))
- {
- indexMarginL = i;
- }
- else if (formatTrimmed.Equals("marginr", StringComparison.Ordinal))
- {
- indexMarginR = i;
- }
- else if (formatTrimmed.Equals("marginv", StringComparison.Ordinal))
- {
- indexMarginV = i;
- }
- else if (formatTrimmed.Equals("effect", StringComparison.Ordinal))
- {
- indexEffect = i;
- }
- else if (formatTrimmed.Equals("layer", StringComparison.Ordinal))
- {
- indexLayer = i;
- }
- }
- }
- else if (!string.IsNullOrEmpty(s))
- {
- var text = string.Empty;
- var start = string.Empty;
- var end = string.Empty;
- var style = string.Empty;
- var actor = string.Empty;
- var marginL = string.Empty;
- var marginR = string.Empty;
- var marginV = string.Empty;
- var effect = string.Empty;
- var layer = 0;
-
- string[] splittedLine;
- if (s.StartsWith("dialog:", StringComparison.Ordinal))
- {
- splittedLine = line.Remove(0, 7).Split(',');
- }
- else if (s.StartsWith("dialogue:", StringComparison.Ordinal))
- {
- splittedLine = line.Remove(0, 9).Split(',');
- }
- else
- {
- splittedLine = line.Split(',');
- }
-
- for (int i = 0; i < splittedLine.Length; i++)
- {
- if (i == indexStart)
- {
- start = splittedLine[i].Trim();
- }
- else if (i == indexEnd)
- {
- end = splittedLine[i].Trim();
- }
- else if (i == indexStyle)
- {
- style = splittedLine[i].Trim();
- }
- else if (i == indexActor && indexName == -1)
- {
- actor = splittedLine[i].Trim();
- }
- else if (i == indexName)
- {
- actor = splittedLine[i].Trim();
- }
- else if (i == indexMarginL)
- {
- marginL = splittedLine[i].Trim();
- }
- else if (i == indexMarginR)
- {
- marginR = splittedLine[i].Trim();
- }
- else if (i == indexMarginV)
- {
- marginV = splittedLine[i].Trim();
- }
- else if (i == indexEffect)
- {
- effect = splittedLine[i].Trim();
- }
- else if (i == indexLayer)
- {
- int.TryParse(splittedLine[i].Replace("Comment:", string.Empty).Trim(), out layer);
- }
- else if (i == indexText)
- {
- text = splittedLine[i];
- }
- else if (i > indexText)
- {
- text += "," + splittedLine[i];
- }
- }
-
- try
- {
- var p = new Paragraph
- {
- StartTime = GetTimeCodeFromString(start),
- EndTime = GetTimeCodeFromString(end),
- Text = GetFormattedText(text)
- };
-
- if (!string.IsNullOrEmpty(style))
- {
- p.Extra = style;
- }
-
- if (!string.IsNullOrEmpty(actor))
- {
- p.Actor = actor;
- }
-
- if (!string.IsNullOrEmpty(marginL))
- {
- p.MarginL = marginL;
- }
-
- if (!string.IsNullOrEmpty(marginR))
- {
- p.MarginR = marginR;
- }
-
- if (!string.IsNullOrEmpty(marginV))
- {
- p.MarginV = marginV;
- }
-
- if (!string.IsNullOrEmpty(effect))
- {
- p.Effect = effect;
- }
-
- p.Layer = layer;
- p.IsComment = s.StartsWith("comment:", StringComparison.Ordinal);
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- if (errors.Length < 2000)
- {
- errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, lineNumber, line));
- }
- else if (subtitle.Paragraphs.Count == 0)
- {
- break;
- }
- }
- }
- }
- }
- if (header.Length > 0)
- {
- subtitle.Header = header.ToString();
- }
-
- if (footer.Length > 0)
- {
- subtitle.Footer = footer.ToString().Trim();
- }
-
- subtitle.Renumber();
- Errors = errors.ToString();
- }
-
- private static TimeCode GetTimeCodeFromString(string time)
- {
- // h:mm:ss.cc
- string[] timeCode = time.Split(':', '.');
- return new TimeCode(int.Parse(timeCode[0]),
- int.Parse(timeCode[1]),
- int.Parse(timeCode[2]),
- int.Parse(timeCode[3]) * 10);
- }
-
- public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat)
- {
- if (newFormat != null && newFormat.Name == SubStationAlpha.NameOfFormat)
- {
- foreach (var p in subtitle.Paragraphs)
- {
- string s = p.Text;
-
- if (s.Contains('{') && s.Contains('}'))
- {
- var p1Index = s.IndexOf("\\p1", StringComparison.Ordinal);
- var p0Index = s.IndexOf("{\\p0}", StringComparison.Ordinal);
- if (p1Index > 0 && (p0Index > p1Index || p0Index == -1))
- {
- var startTagIndex = s.Substring(0, p1Index).LastIndexOf('{');
- if (startTagIndex >= 0)
- {
- if (p0Index > p1Index)
- {
- s = s.Remove(startTagIndex, p0Index - startTagIndex + "{\\p0}".Length);
- }
- else
- {
- s = s.Remove(startTagIndex);
- }
- }
- }
-
- var karaokeStart = s.IndexOf("{Kara Effector", StringComparison.Ordinal);
- if (karaokeStart >= 0)
- {
- int l = s.IndexOf('}', karaokeStart + 1);
- if (l < karaokeStart)
- {
- break;
- }
-
- s = s.Remove(karaokeStart, l - karaokeStart + 1);
- }
-
- s = s.Replace(@"\u0", string.Empty);
- s = s.Replace(@"\u1", string.Empty);
- s = s.Replace(@"\s0", string.Empty);
- s = s.Replace(@"\s1", string.Empty);
- s = s.Replace(@"\be0", string.Empty);
- s = s.Replace(@"\be1", string.Empty);
-
- s = RemoveTag(s, "shad");
- s = RemoveTag(s, "fsc");
- s = RemoveTag(s, "fsp");
- s = RemoveTag(s, "fr");
-
- s = RemoveTag(s, "t(");
- s = RemoveTag(s, "move(");
- s = RemoveTag(s, "Position(");
- s = RemoveTag(s, "org(");
- s = RemoveTag(s, "fade(");
- s = RemoveTag(s, "fad(");
- s = RemoveTag(s, "clip(");
- s = RemoveTag(s, "iclip(");
- s = RemoveTag(s, "pbo(");
- s = RemoveTag(s, "bord");
- s = RemoveTag(s, "pos");
-
- // TODO: Alignment tags
-
- s = s.Replace("{}", string.Empty);
-
- p.Text = s;
- }
- }
- }
- else
- {
- foreach (var p in subtitle.Paragraphs)
- {
- var noTags = Utilities.RemoveSsaTags(p.Text).Trim();
- if (noTags.Length == 0)
- {
- p.Text = string.Empty;
- continue;
- }
-
- p.Text = p.Text.Replace("\\n", Environment.NewLine); // Soft line break
- p.Text = p.Text.Replace("\\N", Environment.NewLine); // Hard line break
- p.Text = p.Text.Replace("\\h", " "); // Hard space
-
- if (noTags.StartsWith("m ", StringComparison.Ordinal))
- {
- var test = noTags.Remove(0, 2)
- .RemoveChar('0')
- .RemoveChar('1')
- .RemoveChar('2')
- .RemoveChar('3')
- .RemoveChar('4')
- .RemoveChar('5')
- .RemoveChar('6')
- .RemoveChar('7')
- .RemoveChar('8')
- .RemoveChar('9')
- .RemoveChar('-')
- .RemoveChar('l')
- .RemoveChar('m')
- .RemoveChar(' ')
- .RemoveChar('.');
- if (test.Length == 0)
- {
- p.Text = string.Empty;
- continue;
- }
- }
-
- int indexOfBegin = p.Text.IndexOf('{');
- string pre = string.Empty;
- while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin)
- {
- string s = p.Text.Substring(indexOfBegin);
- if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an2}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an3}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an4}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an5}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an6}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an7}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an8}", StringComparison.Ordinal) ||
- s.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- pre = s.Substring(0, 6);
- }
- else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an2\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an3\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an4\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an5\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an6\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an7\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an8\\", StringComparison.Ordinal) ||
- s.StartsWith("{\\an9\\", StringComparison.Ordinal))
- {
- pre = s.Substring(0, 5) + "}";
- }
- int indexOfEnd = p.Text.IndexOf('}');
- p.Text = p.Text.Remove(indexOfBegin, indexOfEnd - indexOfBegin + 1);
-
- indexOfBegin = p.Text.IndexOf('{');
- }
- p.Text = pre + p.Text;
- }
- }
- }
-
- private static string RemoveTag(string s, string tag)
- {
- int indexOfTag = s.IndexOf(@"\" + tag, StringComparison.Ordinal);
- if (indexOfTag > 0)
- {
- var endIndex1 = s.IndexOf('\\', indexOfTag + 1);
- var endIndex2 = s.IndexOf('}', indexOfTag + 1);
- endIndex1 = Math.Min(endIndex1, endIndex2);
- if (endIndex1 > 0)
- {
- return s.Remove(indexOfTag, endIndex1 - indexOfTag);
- }
- }
- return s;
- }
-
- ///
- /// BGR color like this: &HBBGGRR& (where BB, GG, and RR are hex values in uppercase)
- ///
- /// Input string
- /// Default color
- /// Input string as color, or default color if problems
- public static Color GetSsaColor(string f, Color defaultColor)
- {
- //Red = &H0000FF&
- //Green = &H00FF00&
- //Blue = &HFF0000&
- //White = &HFFFFFF&
- //Black = &H000000&
- string s = f.Trim().Trim('&');
-
- if (s.StartsWith('h') && s.Length < 7)
- {
- while (s.Length < 7)
- {
- s = s.Insert(1, "0");
- }
- }
-
- if (s.StartsWith('h') && s.Length == 7)
- {
- s = s.Substring(1);
- string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
- try
- {
- return ColorTranslator.FromHtml(hexColor);
- }
- catch
- {
- return defaultColor;
- }
- }
- if (s.StartsWith('h') && s.Length == 9)
- {
- if (int.TryParse(s.Substring(1, 2), NumberStyles.HexNumber, null, out var alpha))
- {
- alpha = 255 - alpha; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
- }
- else
- {
- alpha = 255; // full color
- }
- s = s.Substring(3);
- string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
- try
- {
- var c = ColorTranslator.FromHtml(hexColor);
- return Color.FromArgb(alpha, c);
- }
- catch
- {
- return defaultColor;
- }
- }
-
- if (int.TryParse(f, out var number))
- {
- var temp = Color.FromArgb(number);
- return Color.FromArgb(255, temp.B, temp.G, temp.R);
- }
- return defaultColor;
- }
-
- public static string GetSsaColorString(Color c)
- {
- return $"&H{255 - c.A:X2}{c.B:X2}{c.G:X2}{c.R:X2}"; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
- }
-
- public static string CheckForErrors(string header)
- {
- if (string.IsNullOrEmpty(header))
- {
- return string.Empty;
- }
-
- var sb = new StringBuilder();
-
- int styleCount = -1;
-
- int nameIndex = -1;
- int fontNameIndex = -1;
- int fontsizeIndex = -1;
- int primaryColourIndex = -1;
- int secondaryColourIndex = -1;
- int outlineColourIndex = -1;
- int backColourIndex = -1;
- int boldIndex = -1;
- int italicIndex = -1;
- int underlineIndex = -1;
- int outlineIndex = -1;
- int shadowIndex = -1;
- int alignmentIndex = -1;
- int marginLIndex = -1;
- int marginRIndex = -1;
- int marginVIndex = -1;
- int borderStyleIndex = -1;
-
- foreach (string line in header.SplitToLines())
- {
- string s = line.Trim().ToLowerInvariant();
- if (s.StartsWith("format:", StringComparison.Ordinal))
- {
- if (line.Length > 10)
- {
- var format = line.Substring(8).ToLowerInvariant().Split(',');
- styleCount = format.Length;
- for (int i = 0; i < format.Length; i++)
- {
- string f = format[i].Trim();
- if (f == "name")
- {
- nameIndex = i;
- }
- else if (f == "fontname")
- {
- fontNameIndex = i;
- }
- else if (f == "fontsize")
- {
- fontsizeIndex = i;
- }
- else if (f == "primarycolour")
- {
- primaryColourIndex = i;
- }
- else if (f == "secondarycolour")
- {
- secondaryColourIndex = i;
- }
- else if (f == "outlinecolour")
- {
- outlineColourIndex = i;
- }
- else if (f == "backcolour")
- {
- backColourIndex = i;
- }
- else if (f == "bold")
- {
- boldIndex = i;
- }
- else if (f == "italic")
- {
- italicIndex = i;
- }
- else if (f == "underline")
- {
- underlineIndex = i;
- }
- else if (f == "outline")
- {
- outlineIndex = i;
- }
- else if (f == "shadow")
- {
- shadowIndex = i;
- }
- else if (f == "alignment")
- {
- alignmentIndex = i;
- }
- else if (f == "marginl")
- {
- marginLIndex = i;
- }
- else if (f == "marginr")
- {
- marginRIndex = i;
- }
- else if (f == "marginv")
- {
- marginVIndex = i;
- }
- else if (f == "borderstyle")
- {
- borderStyleIndex = i;
- }
- }
- }
- }
- else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
- {
- if (line.Length > 10)
- {
- string rawLine = line;
- var format = line.Substring(6).Split(',');
-
- if (format.Length != styleCount)
- {
- sb.AppendLine("Number of expected Style elements do not match number of Format elements: " + rawLine);
- sb.AppendLine();
- }
- else
- {
- var dummyColor = Color.FromArgb(9, 14, 16, 26);
- for (int i = 0; i < format.Length; i++)
- {
- string f = format[i].Trim().ToLowerInvariant();
- if (i == nameIndex)
- {
- if (f.Length == 0)
- {
- sb.AppendLine("'Name' is empty: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == fontNameIndex)
- {
- if (f.Length == 0)
- {
- sb.AppendLine("'Fontname' is empty: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == fontsizeIndex)
- {
- if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'Fontsize' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == primaryColourIndex)
- {
- if (GetSsaColor(f, dummyColor) == dummyColor || f == "&h")
- {
- sb.AppendLine("'PrimaryColour' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == secondaryColourIndex)
- {
- if (GetSsaColor(f, dummyColor) == dummyColor)
- {
- sb.AppendLine("'SecondaryColour' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == outlineColourIndex)
- {
- if (GetSsaColor(f, dummyColor) == dummyColor)
- {
- sb.AppendLine("'OutlineColour' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == backColourIndex)
- {
- if (GetSsaColor(f, dummyColor) == dummyColor)
- {
- sb.AppendLine("'BackColour' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == boldIndex)
- {
- if (Utilities.AllLetters.Contains(f))
- {
- sb.AppendLine("'Bold' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == italicIndex)
- {
- if (Utilities.AllLetters.Contains(f))
- {
- sb.AppendLine("'Italic' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == underlineIndex)
- {
- if (Utilities.AllLetters.Contains(f))
- {
- sb.AppendLine("'Underline' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == outlineIndex)
- {
- if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'Outline' (width) incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == shadowIndex)
- {
- if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'Shadow' (width) incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == alignmentIndex)
- {
- if (!"101123456789 ".Contains(f))
- {
- sb.AppendLine("'Alignment' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == marginLIndex)
- {
- if (!int.TryParse(f, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'MarginL' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == marginRIndex)
- {
- if (!int.TryParse(f, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'MarginR' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == marginVIndex)
- {
- if (!int.TryParse(f, out _) || f.StartsWith('-'))
- {
- sb.AppendLine("'MarginV' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- else if (i == borderStyleIndex)
- {
- if (f.Length != 0 && !"123".Contains(f))
- {
- sb.AppendLine("'BorderStyle' incorrect: " + rawLine);
- sb.AppendLine();
- }
- }
- }
- }
- }
- }
- }
- return sb.ToString();
- }
-
- ///
- /// Add new style to ASS header
- ///
- /// Header with new style
- public static string AddSsaStyle(SsaStyle style, string inputHeader)
- {
- var header = inputHeader;
- if (string.IsNullOrEmpty(header))
- {
- header = DefaultHeader;
- }
-
- var sb = new StringBuilder();
- bool stylesStarted = false;
- bool styleAdded = false;
- string styleFormat = "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding";
- foreach (string line in header.SplitToLines())
- {
- if (line.Equals("[V4+ Styles]", StringComparison.OrdinalIgnoreCase) || line.Equals("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
- {
- stylesStarted = true;
- }
-
- if (line.StartsWith("format:", StringComparison.OrdinalIgnoreCase))
- {
- styleFormat = line;
- }
-
- if (!line.StartsWith("Style: " + style.Name + ",", StringComparison.Ordinal)) // overwrite existing style
- {
- sb.AppendLine(line);
- }
-
- if (!styleAdded && stylesStarted && line.TrimStart().StartsWith("style:", StringComparison.OrdinalIgnoreCase))
- {
- sb.AppendLine(style.ToRawAss(styleFormat));
- styleAdded = true;
- }
- }
- return sb.ToString();
- }
-
- public static SsaStyle GetSsaStyle(string styleName, string header)
- {
- var style = new SsaStyle { Name = styleName };
-
- int nameIndex = -1;
- int fontNameIndex = -1;
- int fontsizeIndex = -1;
- int primaryColourIndex = -1;
- int secondaryColourIndex = -1;
- int tertiaryColourIndex = -1;
- int outlineColourIndex = -1;
- int backColourIndex = -1;
- int boldIndex = -1;
- int italicIndex = -1;
- int underlineIndex = -1;
- int outlineIndex = -1;
- int shadowIndex = -1;
- int alignmentIndex = -1;
- int marginLIndex = -1;
- int marginRIndex = -1;
- int marginVIndex = -1;
- int borderStyleIndex = -1;
-
- if (header == null)
- {
- header = DefaultHeader;
- }
-
- foreach (string line in header.SplitToLines())
- {
- string s = line.Trim().ToLowerInvariant();
- if (s.StartsWith("format:", StringComparison.Ordinal))
- {
- if (line.Length > 10)
- {
- var format = line.ToLowerInvariant().Substring(8).Split(',');
- for (int i = 0; i < format.Length; i++)
- {
- string f = format[i].Trim().ToLowerInvariant();
- if (f == "name")
- {
- nameIndex = i;
- }
- else if (f == "fontname")
- {
- fontNameIndex = i;
- }
- else if (f == "fontsize")
- {
- fontsizeIndex = i;
- }
- else if (f == "primarycolour")
- {
- primaryColourIndex = i;
- }
- else if (f == "secondarycolour")
- {
- secondaryColourIndex = i;
- }
- else if (f == "tertiarycolour")
- {
- tertiaryColourIndex = i;
- }
- else if (f == "outlinecolour")
- {
- outlineColourIndex = i;
- }
- else if (f == "backcolour")
- {
- backColourIndex = i;
- }
- else if (f == "bold")
- {
- boldIndex = i;
- }
- else if (f == "italic")
- {
- italicIndex = i;
- }
- else if (f == "underline")
- {
- underlineIndex = i;
- }
- else if (f == "outline")
- {
- outlineIndex = i;
- }
- else if (f == "shadow")
- {
- shadowIndex = i;
- }
- else if (f == "alignment")
- {
- alignmentIndex = i;
- }
- else if (f == "marginl")
- {
- marginLIndex = i;
- }
- else if (f == "marginr")
- {
- marginRIndex = i;
- }
- else if (f == "marginv")
- {
- marginVIndex = i;
- }
- else if (f == "borderstyle")
- {
- borderStyleIndex = i;
- }
- }
- }
- }
- else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
- {
- if (line.Length > 10)
- {
- style.RawLine = line;
- var format = line.Substring(6).Split(',');
- for (int i = 0; i < format.Length; i++)
- {
- string f = format[i].Trim().ToLowerInvariant();
- if (i == nameIndex)
- {
- style.Name = format[i].Trim();
- }
- else if (i == fontNameIndex)
- {
- style.FontName = f;
- }
- else if (i == fontsizeIndex)
- {
- if (float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var fOut))
- {
- style.FontSize = fOut;
- }
- }
- else if (i == primaryColourIndex)
- {
- style.Primary = GetSsaColor(f, Color.White);
- }
- else if (i == secondaryColourIndex)
- {
- style.Secondary = GetSsaColor(f, Color.Yellow);
- }
- else if (i == tertiaryColourIndex)
- {
- style.Tertiary = GetSsaColor(f, Color.Yellow);
- }
- else if (i == outlineColourIndex)
- {
- style.Outline = GetSsaColor(f, Color.Black);
- }
- else if (i == backColourIndex)
- {
- style.Background = GetSsaColor(f, Color.Black);
- }
- else if (i == boldIndex)
- {
- style.Bold = f == "-1" || f == "1";
- }
- else if (i == italicIndex)
- {
- style.Italic = f == "-1" || f == "1";
- }
- else if (i == underlineIndex)
- {
- style.Underline = f == "-1" || f == "1";
- }
- else if (i == outlineIndex)
- {
- if (decimal.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var number))
- {
- style.OutlineWidth = number;
- }
- }
- else if (i == shadowIndex)
- {
- if (decimal.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var number))
- {
- style.ShadowWidth = number;
- }
- }
- else if (i == alignmentIndex)
- {
- style.Alignment = f;
- }
- else if (i == marginLIndex)
- {
- if (int.TryParse(f, out var number))
- {
- style.MarginLeft = number;
- }
- }
- else if (i == marginRIndex)
- {
- if (int.TryParse(f, out var number))
- {
- style.MarginRight = number;
- }
- }
- else if (i == marginVIndex)
- {
- if (int.TryParse(f, out var number))
- {
- style.MarginVertical = number;
- }
- }
- else if (i == borderStyleIndex)
- {
- style.BorderStyle = f;
- }
- }
- }
- if (styleName != null && style.Name != null && (styleName.Equals(style.Name, StringComparison.OrdinalIgnoreCase) ||
- styleName.Equals("*Default", StringComparison.OrdinalIgnoreCase) &&
- style.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)))
- {
- style.LoadedFromHeader = true;
- return style;
- }
- }
- }
- return new SsaStyle { Name = styleName };
- }
-
- public override bool HasStyleSupport => true;
- }
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class AdvancedSubStationAlpha : SubtitleFormat
+ {
+ public string Errors { get; private set; }
+
+ public static string DefaultStyle
+ {
+ get
+ {
+ var borderStyle = "1"; // 1=normal, 3=opaque box
+ if (Configuration.Settings.SubtitleSettings.SsaOpaqueBox)
+ {
+ borderStyle = "3";
+ }
+
+ var boldStyle = "0"; // 0=regular
+ if (Configuration.Settings.SubtitleSettings.SsaFontBold)
+ {
+ boldStyle = "-1";
+ }
+
+ var ssa = Configuration.Settings.SubtitleSettings;
+ return "Style: Default," + ssa.SsaFontName + "," +
+ ssa.SsaFontSize.ToString(CultureInfo.InvariantCulture) + "," +
+ GetSsaColorString(Color.FromArgb(ssa.SsaFontColorArgb)) + "," +
+ "&H0300FFFF,&H00000000,&H02000000," + boldStyle + ",0,0,0,100,100,0,0," + borderStyle + "," + ssa.SsaOutline.ToString(CultureInfo.InvariantCulture) + "," +
+ Configuration.Settings.SubtitleSettings.SsaShadow.ToString(CultureInfo.InvariantCulture) + ",2," + ssa.SsaMarginLeft + "," + ssa.SsaMarginRight + "," + ssa.SsaMarginTopBottom + ",1";
+ }
+ }
+
+ public static string DefaultHeader
+ {
+ get
+ {
+ var format = new AdvancedSubStationAlpha();
+ var sub = new Subtitle();
+ string text = format.ToText(sub, string.Empty);
+ var lines = text.SplitToLines();
+ format.LoadSubtitle(sub, lines, string.Empty);
+ return sub.Header;
+ }
+ }
+
+ public override string Extension => ".ass";
+
+ public const string NameOfFormat = "Advanced Sub Station Alpha";
+
+ public override string Name => NameOfFormat;
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var subtitle = new Subtitle();
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string all = sb.ToString();
+ if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".ass", StringComparison.OrdinalIgnoreCase) && !all.Contains("[V4 Styles]"))
+ {
+ }
+ else if (!all.Contains("dialog:", StringComparison.OrdinalIgnoreCase) && !all.Contains("dialogue:", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ else if (!all.Contains("[V4+ Styles]") && new SubStationAlpha().IsMine(lines, fileName))
+ {
+ return false;
+ }
+
+ LoadSubtitle(subtitle, lines, fileName);
+ Errors = null;
+ if (subtitle.Paragraphs.Count > _errorCount)
+ {
+ if (!string.IsNullOrEmpty(subtitle.Header))
+ {
+ subtitle.Header = subtitle.Header.Replace("[V4 Styles]", "[V4+ Styles]");
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ public const string HeaderNoStyles = @"[Script Info]
+; This is an Advanced Sub Station Alpha v4+ script.
+Title: {0}
+ScriptType: v4.00+
+Collisions: Normal
+PlayDepth: 0
+
+[V4+ Styles]
+Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
+{1}
+
+[Events]
+Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ bool fromTtml = false;
+ string header = @"[Script Info]
+; This is an Advanced Sub Station Alpha v4+ script.
+Title: {0}
+ScriptType: v4.00+
+Collisions: Normal
+PlayDepth: 0
+
+[V4+ Styles]
+Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
+" + DefaultStyle + @"
+
+[Events]
+Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text";
+
+ const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc
+ const string paragraphWriteFormat = "Dialogue: {9},{0},{1},{3},{4},{5},{6},{7},{8},{2}";
+ const string commentWriteFormat = "Comment: {9},{0},{1},{3},{4},{5},{6},{7},{8},{2}";
+
+ var sb = new StringBuilder();
+ bool isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
+ var styles = new List();
+ if (isValidAssHeader)
+ {
+ sb.AppendLine(subtitle.Header.Trim());
+ sb.AppendLine("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+ styles = GetStylesFromHeader(subtitle.Header);
+ }
+ else if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4 Styles]"))
+ {
+ LoadStylesFromSubstationAlpha(subtitle, title, header, HeaderNoStyles, sb);
+ }
+ else if (subtitle.Header != null && subtitle.Header.Contains("http://www.w3.org/ns/ttml"))
+ {
+ LoadStylesFromTimedText10(subtitle, title, header, HeaderNoStyles, sb);
+ fromTtml = true;
+ isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
+ if (isValidAssHeader)
+ {
+ styles = GetStylesFromHeader(subtitle.Header);
+ }
+ }
+ else if (subtitle.Header != null && subtitle.Header.Contains("http://www.w3.org/2006/10/ttaf1"))
+ {
+ LoadStylesFromTimedTextTimedDraft2006Oct(subtitle, title, header, HeaderNoStyles, sb);
+ fromTtml = true;
+ isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
+ if (isValidAssHeader)
+ {
+ styles = GetStylesFromHeader(subtitle.Header);
+ }
+ }
+ else
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+ foreach (var p in subtitle.Paragraphs)
+ {
+ string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10);
+ string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10);
+ string style = "Default";
+ if (!string.IsNullOrEmpty(p.Extra) && isValidAssHeader && styles.Contains(p.Extra))
+ {
+ style = p.Extra;
+ }
+
+ if (fromTtml && !string.IsNullOrEmpty(p.Style) && isValidAssHeader && styles.Contains(p.Style))
+ {
+ style = p.Style;
+ }
+
+ string actor = "";
+ if (!string.IsNullOrEmpty(p.Actor))
+ {
+ actor = p.Actor;
+ }
+
+ string marginL = "0";
+ if (!string.IsNullOrEmpty(p.MarginL) && Utilities.IsInteger(p.MarginL))
+ {
+ marginL = p.MarginL;
+ }
+
+ string marginR = "0";
+ if (!string.IsNullOrEmpty(p.MarginR) && Utilities.IsInteger(p.MarginR))
+ {
+ marginR = p.MarginR;
+ }
+
+ string marginV = "0";
+ if (!string.IsNullOrEmpty(p.MarginV) && Utilities.IsInteger(p.MarginV))
+ {
+ marginV = p.MarginV;
+ }
+
+ string effect = "";
+ if (!string.IsNullOrEmpty(p.Effect))
+ {
+ effect = p.Effect;
+ }
+
+ if (p.IsComment)
+ {
+ sb.AppendLine(string.Format(commentWriteFormat, start, end, FormatText(p), style, actor, marginL, marginR, marginV, effect, p.Layer));
+ }
+ else
+ {
+ sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style, actor, marginL, marginR, marginV, effect, p.Layer));
+ }
+ }
+
+ if (!string.IsNullOrEmpty(subtitle.Footer) &&
+ (subtitle.Footer.Contains("[Fonts]" + Environment.NewLine) || subtitle.Footer.Contains("[Graphics]" + Environment.NewLine) || subtitle.Footer.Contains("[Aegisub Extradata]" + Environment.NewLine)))
+ {
+ sb.AppendLine();
+ sb.AppendLine(subtitle.Footer);
+ }
+ return sb.ToString().Trim() + Environment.NewLine;
+ }
+
+ public static string GetHeaderAndStylesFromSubStationAlpha(string header)
+ {
+ var scriptInfo = string.Empty;
+ if (header != null &&
+ header.Contains("[Script Info]") &&
+ header.Contains("ScriptType: v4.00") &&
+ !header.Contains("ScriptType: v4.00+"))
+ {
+ var sb = new StringBuilder();
+ var scriptInfoOn = false;
+ foreach (var line in header.SplitToLines())
+ {
+ if (line.RemoveChar(' ').Contains("Styles]", StringComparison.OrdinalIgnoreCase))
+ {
+ break;
+ }
+
+ if (line.Equals("[Script Info]", StringComparison.OrdinalIgnoreCase))
+ {
+ scriptInfoOn = true;
+ }
+
+ if (scriptInfoOn)
+ {
+ if (line.StartsWith("ScriptType:", StringComparison.OrdinalIgnoreCase))
+ {
+ sb.AppendLine("ScriptType: v4.00+");
+ }
+ else if (line.Equals("; This is a Sub Station Alpha v4 script.", StringComparison.OrdinalIgnoreCase))
+ {
+ sb.AppendLine("; This is an Advanced Sub Station Alpha v4+ script.");
+ }
+ else
+ {
+ sb.AppendLine(line);
+ }
+ }
+ }
+ scriptInfo = sb.ToString();
+ }
+
+ var style = GetStyle(header);
+
+ if (string.IsNullOrEmpty(scriptInfo) || string.IsNullOrEmpty(style))
+ {
+ return DefaultHeader;
+ }
+
+ return string.Format($@"{scriptInfo.Trim() + Environment.NewLine}
+[V4+ Styles]
+Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
+{style.Trim() + Environment.NewLine}
+[Events]");
+ }
+
+ private static void LoadStylesFromSubstationAlpha(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
+ {
+ try
+ {
+ var ttStyles = GetStyle(subtitle.Header);
+ if (!string.IsNullOrEmpty(ttStyles))
+ {
+ sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
+ subtitle.Header = sb.ToString();
+ }
+ else
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+ }
+ catch
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+ }
+
+ private static string GetStyle(string header)
+ {
+ var ttStyles = new StringBuilder();
+
+ // Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
+ const string styleFormat = "Style: {0},{1},{2},{3},{4},{5},{6},{7},{8},{9},0,100,100,0,0,{10},{11},{12},{13},{14},{15},{16},1";
+ foreach (var styleName in GetStylesFromHeader(header))
+ {
+ try
+ {
+ var ssaStyle = GetSsaStyle(styleName, header);
+
+ string bold = "0";
+ if (ssaStyle.Bold)
+ {
+ bold = "-1";
+ }
+
+ string italic = "0";
+ if (ssaStyle.Italic)
+ {
+ italic = "-1";
+ }
+
+ string underline = "0";
+ if (ssaStyle.Underline)
+ {
+ underline = "-1";
+ }
+
+ string newAlignment = "2";
+ switch (ssaStyle.Alignment)
+ {
+ case "1":
+ newAlignment = "1";
+ break;
+ case "3":
+ newAlignment = "3";
+ break;
+ case "9":
+ newAlignment = "4";
+ break;
+ case "10":
+ newAlignment = "5";
+ break;
+ case "11":
+ newAlignment = "6";
+ break;
+ case "5":
+ newAlignment = "7";
+ break;
+ case "6":
+ newAlignment = "8";
+ break;
+ case "7":
+ newAlignment = "9";
+ break;
+ }
+
+ ttStyles.AppendLine(string.Format(styleFormat, ssaStyle.Name, ssaStyle.FontName, ssaStyle.FontSize, GetSsaColorString(ssaStyle.Primary), GetSsaColorString(ssaStyle.Secondary),
+ GetSsaColorString(ssaStyle.Outline), GetSsaColorString(ssaStyle.Background), bold, italic, underline, ssaStyle.BorderStyle, ssaStyle.OutlineWidth.ToString(CultureInfo.InvariantCulture), ssaStyle.ShadowWidth.ToString(CultureInfo.InvariantCulture),
+ newAlignment, ssaStyle.MarginLeft, ssaStyle.MarginRight, ssaStyle.MarginVertical));
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ return ttStyles.ToString();
+ }
+
+ public static void LoadStylesFromTimedText10(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
+ {
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ p.Effect = null;
+ }
+
+ try
+ {
+ var lines = subtitle.Header.SplitToLines();
+ var tt = new TimedText10();
+ var sub = new Subtitle();
+ tt.LoadSubtitle(sub, lines, string.Empty);
+
+ var xml = new XmlDocument();
+ xml.LoadXml(subtitle.Header);
+ var nsmgr = new XmlNamespaceManager(xml.NameTable);
+ nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
+ XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
+ int styleCount = 0;
+ var ttStyles = new StringBuilder();
+ var styleNames = new List();
+ foreach (XmlNode node in head.SelectNodes("//ttml:style", nsmgr))
+ {
+ string name = null;
+ if (node.Attributes["xml:id"] != null)
+ {
+ name = node.Attributes["xml:id"].Value;
+ }
+ else if (node.Attributes["id"] != null)
+ {
+ name = node.Attributes["id"].Value;
+ }
+
+ if (name != null)
+ {
+ styleCount++;
+
+ string fontFamily = "Arial";
+ if (node.Attributes["tts:fontFamily"]?.Value != null)
+ {
+ fontFamily = node.Attributes["tts:fontFamily"].Value;
+ if (fontFamily.Contains(","))
+ {
+ fontFamily = fontFamily.Split(',')[0];
+ }
+ }
+
+ string fontWeight = "normal";
+ if (node.Attributes["tts:fontWeight"] != null)
+ {
+ fontWeight = node.Attributes["tts:fontWeight"].Value;
+ }
+
+ string fontStyle = "normal";
+ if (node.Attributes["tts:fontStyle"] != null)
+ {
+ fontStyle = node.Attributes["tts:fontStyle"].Value;
+ }
+
+ string color = "#ffffff";
+ if (node.Attributes["tts:color"] != null)
+ {
+ color = node.Attributes["tts:color"].Value.Trim();
+ }
+
+ Color c = Color.White;
+ try
+ {
+ if (color.StartsWith("rgb(", StringComparison.Ordinal))
+ {
+ string[] arr = color.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ c = Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
+ }
+ else
+ {
+ c = ColorTranslator.FromHtml(color);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+
+ string fontSize = "20";
+ if (node.Attributes["tts:fontSize"] != null)
+ {
+ fontSize = node.Attributes["tts:fontSize"].Value.Replace("px", string.Empty).Replace("em", string.Empty);
+ }
+
+ if (!float.TryParse(fontSize, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var fSize))
+ {
+ fSize = 20;
+ }
+
+ string italic = "0";
+ if (fontStyle == "italic")
+ {
+ italic = "-1";
+ }
+
+ string bold = "0";
+ if (fontWeight == "bold")
+ {
+ bold = "-1";
+ }
+
+ const string styleFormat = "Style: {0},{1},{2},{3},&H0300FFFF,&H00000000,&H02000000,{4},{5},0,0,100,100,0,0,1,2,2,2,10,10,10,1";
+ ttStyles.AppendLine(string.Format(styleFormat, name, fontFamily, fSize, GetSsaColorString(c), bold, italic));
+ styleNames.Add(name);
+ }
+ }
+
+ if (styleCount > 0)
+ {
+ if (!styleNames.Contains("Default") && !styleNames.Contains("default") && subtitle.Paragraphs.Any(pa => string.IsNullOrEmpty(pa.Extra)))
+ {
+ ttStyles = new StringBuilder(DefaultStyle + Environment.NewLine + ttStyles);
+ foreach (var paragraph in subtitle.Paragraphs)
+ {
+ if (string.IsNullOrEmpty(paragraph.Extra))
+ {
+ paragraph.Extra = "Default";
+ }
+ }
+ }
+ sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
+ subtitle.Header = sb.ToString();
+ }
+ else
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+
+ // Set correct style on paragraphs
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (p.Extra != null && p.Extra.Contains('/'))
+ {
+ p.Extra = p.Extra.Split('/')[0].Trim();
+ }
+ }
+ }
+ catch
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+ }
+
+ public static void LoadStylesFromTimedTextTimedDraft2006Oct(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
+ {
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ p.Effect = null;
+ }
+
+ try
+ {
+ var lines = subtitle.Header.SplitToLines();
+ var sb2 = new StringBuilder();
+ lines.ForEach(line => sb2.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(sb2.ToString().Replace(" & ", " & ").Replace("Q&A", "Q&A").RemoveControlCharactersButWhiteSpace().Trim());
+ subtitle.Header = xml.OuterXml;
+ var nsmgr = new XmlNamespaceManager(xml.NameTable);
+ nsmgr.AddNamespace("ttaf1", xml.DocumentElement.NamespaceURI);
+ int styleCount = 0;
+ var ttStyles = new StringBuilder();
+ var styleNames = new List();
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("//ttaf1:style", nsmgr))
+ {
+ string name = null;
+ if (node.Attributes["xml:id"] != null)
+ {
+ name = node.Attributes["xml:id"].Value;
+ }
+ else if (node.Attributes["id"] != null)
+ {
+ name = node.Attributes["id"].Value;
+ }
+
+ if (name != null)
+ {
+ styleCount++;
+
+ string fontFamily = "Arial";
+ if (node.Attributes["tts:fontFamily"]?.Value != null)
+ {
+ fontFamily = node.Attributes["tts:fontFamily"].Value;
+ if (fontFamily.Contains(","))
+ {
+ fontFamily = fontFamily.Split(',')[0];
+ }
+ }
+
+ string fontWeight = "normal";
+ if (node.Attributes["tts:fontWeight"] != null)
+ {
+ fontWeight = node.Attributes["tts:fontWeight"].Value;
+ }
+
+ string fontStyle = "normal";
+ if (node.Attributes["tts:fontStyle"] != null)
+ {
+ fontStyle = node.Attributes["tts:fontStyle"].Value;
+ }
+
+ string color = "#ffffff";
+ if (node.Attributes["tts:color"] != null)
+ {
+ color = node.Attributes["tts:color"].Value.Trim();
+ }
+
+ Color c = Color.White;
+ try
+ {
+ if (color.StartsWith("rgb(", StringComparison.Ordinal))
+ {
+ string[] arr = color.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ c = Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
+ }
+ else
+ {
+ c = ColorTranslator.FromHtml(color);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+
+ string fontSize = "20";
+ if (node.Attributes["tts:fontSize"] != null)
+ {
+ fontSize = node.Attributes["tts:fontSize"].Value.Replace("px", string.Empty).Replace("em", string.Empty);
+ }
+
+ if (!float.TryParse(fontSize, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var fSize))
+ {
+ fSize = 20;
+ }
+
+ string italic = "0";
+ if (fontStyle == "italic")
+ {
+ italic = "-1";
+ }
+
+ string bold = "0";
+ if (fontWeight == "bold")
+ {
+ bold = "-1";
+ }
+
+ const string styleFormat = "Style: {0},{1},{2},{3},&H0300FFFF,&H00000000,&H02000000,{4},{5},0,0,100,100,0,0,1,2,2,2,10,10,10,1";
+ ttStyles.AppendLine(string.Format(styleFormat, name, fontFamily, fSize, GetSsaColorString(c), bold, italic));
+ styleNames.Add(name);
+ }
+ }
+
+ if (styleCount > 0)
+ {
+ if (!styleNames.Contains("Default") && !styleNames.Contains("default") && subtitle.Paragraphs.Any(pa => string.IsNullOrEmpty(pa.Extra)))
+ {
+ ttStyles = new StringBuilder(DefaultStyle + Environment.NewLine + ttStyles);
+ foreach (var paragraph in subtitle.Paragraphs)
+ {
+ if (string.IsNullOrEmpty(paragraph.Extra))
+ {
+ paragraph.Extra = "Default";
+ }
+ }
+ }
+ sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
+ subtitle.Header = sb.ToString();
+ }
+ else
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+
+ // Set correct style on paragraphs
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (p.Extra != null && p.Extra.Contains('/'))
+ {
+ p.Extra = p.Extra.Split('/')[0].Trim();
+ }
+ }
+ }
+ catch
+ {
+ sb.AppendLine(string.Format(header, title));
+ }
+ }
+
+ public static List GetStylesFromHeader(string headerLines)
+ {
+ var list = new List();
+
+ if (headerLines == null)
+ {
+ headerLines = DefaultStyle;
+ }
+
+ if (headerLines.Contains("http://www.w3.org/ns/ttml"))
+ {
+ var subtitle = new Subtitle { Header = headerLines };
+ LoadStylesFromTimedText10(subtitle, string.Empty, headerLines, HeaderNoStyles, new StringBuilder());
+ headerLines = subtitle.Header;
+ }
+
+ foreach (string line in headerLines.SplitToLines())
+ {
+ if (line.StartsWith("style:", StringComparison.OrdinalIgnoreCase))
+ {
+ int end = line.IndexOf(',');
+ if (end > 0)
+ {
+ list.Add(line.Substring(6, end - 6).Trim());
+ }
+ }
+ }
+ return list;
+ }
+
+ public static string FormatText(Paragraph p)
+ {
+ string text = p.Text.Replace(Environment.NewLine, "\\N");
+
+ if (!text.Contains('<'))
+ {
+ return text;
+ }
+
+ text = text.Replace("", @"{\i1}");
+ text = text.Replace("", @"{\i0}");
+ text = text.Replace("", @"{\i}");
+ text = text.Replace("", @"{\u1}");
+ text = text.Replace("", @"{\u0}");
+ text = text.Replace("", @"{\u}");
+ text = text.Replace("", @"{\b1}");
+ text = text.Replace("", @"{\b0}");
+ text = text.Replace("", @"{\b}");
+ int count = 0;
+ while (text.Contains("', start);
+ if (end > 0)
+ {
+ string fontTag = text.Substring(start + 5, end - (start + 4));
+ text = text.Remove(start, end - start + 1);
+ int indexOfEndFont = text.IndexOf("", start, StringComparison.Ordinal);
+ if (indexOfEndFont > 0)
+ {
+ text = text.Remove(indexOfEndFont, 7);
+ if (indexOfEndFont < text.Length)
+ {
+ if (fontTag.Contains(" size="))
+ {
+ text = text.Insert(indexOfEndFont, "{\\fs}");
+ }
+ if (fontTag.Contains(" face="))
+ {
+ text = text.Insert(indexOfEndFont, "{\\fn}");
+ }
+ if (fontTag.Contains(" color="))
+ {
+ text = text.Insert(indexOfEndFont, "{\\c}");
+ }
+ }
+ }
+
+ fontTag = FormatTag(ref text, start, fontTag, "face=\"", "fn", "}");
+ fontTag = FormatTag(ref text, start, fontTag, "face='", "fn", "}");
+ fontTag = FormatTag(ref text, start, fontTag, "face=", "fn", "}");
+
+ fontTag = FormatTag(ref text, start, fontTag, "size=\"", "fs", "}");
+ fontTag = FormatTag(ref text, start, fontTag, "size='", "fs", "}");
+ fontTag = FormatTag(ref text, start, fontTag, "size=", "fs", "}");
+
+ fontTag = FormatTag(ref text, start, fontTag, "color=\"", "c&H", "&}");
+ fontTag = FormatTag(ref text, start, fontTag, "color='", "c&H", "&}");
+ FormatTag(ref text, start, fontTag, "color=", "c&H", "&}");
+ }
+ count++;
+ }
+ text = text.Replace("{\\c}", "@___@@").Replace("}{", string.Empty).Replace("@___@@", "{\\c}").Replace("{\\c}{\\c&", "{\\c&");
+ while (text.EndsWith("{\\c}", StringComparison.Ordinal))
+ {
+ text = text.Remove(text.Length - 4);
+ }
+ while (text.Contains("\\fs\\fs", StringComparison.Ordinal))
+ {
+ text = text.Replace("\\fs\\fs", "\\fs");
+ }
+ while (text.Contains("\\fn\\fn", StringComparison.Ordinal))
+ {
+ text = text.Replace("\\fn\\fn", "\\fn");
+ }
+ while (text.Contains("\\c\\c&H", StringComparison.Ordinal))
+ {
+ text = text.Replace("\\c\\c&H", "\\c&H");
+ }
+ return text;
+ }
+
+ private static string FormatTag(ref string text, int start, string fontTag, string tag, string ssaTagName, string endSsaTag)
+ {
+ if (fontTag.Contains(tag))
+ {
+ int fontStart = fontTag.IndexOf(tag, StringComparison.Ordinal);
+
+ int fontEnd = fontTag.IndexOfAny(new[] { '"', '\'' }, fontStart + tag.Length);
+ if (fontEnd < 0)
+ {
+ fontEnd = fontTag.IndexOfAny(new[] { ' ', '>' }, fontStart + tag.Length);
+ }
+
+ if (fontEnd > 0)
+ {
+ string subTag = fontTag.Substring(fontStart + tag.Length, fontEnd - (fontStart + tag.Length));
+ if (tag.Contains("color"))
+ {
+ Color c;
+ try
+ {
+ c = ColorTranslator.FromHtml(subTag);
+ }
+ catch
+ {
+ c = Color.White;
+ }
+ subTag = (c.B.ToString("X2") + c.G.ToString("X2") + c.R.ToString("X2")).ToLowerInvariant(); // use bbggrr
+ }
+ fontTag = fontTag.Remove(fontStart, fontEnd - fontStart + 1);
+ if (start < text.Length)
+ {
+ text = text.Insert(start, @"{\" + ssaTagName + subTag + endSsaTag);
+ }
+ }
+ }
+ return fontTag;
+ }
+
+ public static string GetFormattedText(string input)
+ {
+ var text = input.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine);
+
+ var tooComplex = ContainsUnsupportedTags(text);
+
+ if (!tooComplex)
+ {
+ for (int i = 0; i < 10; i++) // just look ten times...
+ {
+ bool italic;
+ if (text.Contains(@"{\fn"))
+ {
+ int start = text.IndexOf(@"{\fn", StringComparison.Ordinal);
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal))
+ {
+ string fontName = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref fontName, ref extraTags, out var unknownTags, out italic);
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ {
+ text = text.Insert(start, "" + unknownTags + "");
+ }
+ else
+ {
+ text = text.Insert(start, "" + unknownTags);
+ }
+
+ int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal);
+ if (indexOfEndTag > 0)
+ {
+ text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "");
+ }
+ else
+ {
+ int indexOfNextTag1 = text.IndexOf("{\\fn", start, StringComparison.Ordinal);
+ int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
+ if (indexOfNextTag1 > 0)
+ {
+ text = text.Insert(indexOfNextTag1, "");
+ }
+ else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
+ {
+ text = text.Insert(indexOfNextTag2, "");
+ }
+ else
+ {
+ text += "";
+ }
+ }
+ }
+ }
+
+ if (text.Contains(@"{\fs"))
+ {
+ int start = text.IndexOf(@"{\fs", StringComparison.Ordinal);
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal))
+ {
+ string fontSize = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref fontSize, ref extraTags, out var unknownTags, out italic);
+ if (float.TryParse(fontSize, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _))
+ {
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ {
+ text = text.Insert(start, "" + unknownTags + "");
+ }
+ else
+ {
+ text = text.Insert(start, "" + unknownTags);
+ }
+
+ int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal);
+ if (indexOfEndTag > 0)
+ {
+ text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "");
+ }
+ else
+ {
+ int indexOfNextTag1 = text.IndexOf("{\\fs", start, StringComparison.Ordinal);
+ int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
+ if (indexOfNextTag1 > 0)
+ {
+ text = text.Insert(indexOfNextTag1, "");
+ }
+ else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
+ {
+ text = text.Insert(indexOfNextTag2, "");
+ }
+ else
+ {
+ text += "";
+ }
+ }
+ }
+ }
+ }
+
+ if (text.Contains(@"{\c"))
+ {
+ int start = text.IndexOf(@"{\c", StringComparison.Ordinal);
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal) && !text.Substring(start).StartsWith("{\\clip", StringComparison.Ordinal))
+ {
+ string color = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref color, ref extraTags, out var unknownTags, out italic);
+
+ color = color.RemoveChar('&').TrimStart('H');
+ color = color.PadLeft(6, '0');
+
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLowerInvariant();
+
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ {
+ text = text.Insert(start, "" + unknownTags + "");
+ }
+ else
+ {
+ text = text.Insert(start, "" + unknownTags);
+ }
+
+ int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
+ int indexOfNextColorTag = text.IndexOf("{\\c&", start, StringComparison.Ordinal);
+ if (indexOfNextColorTag > 0 && (indexOfNextColorTag < indexOfEndTag || indexOfEndTag == -1))
+ {
+ text = text.Insert(indexOfNextColorTag, "");
+ }
+ else if (indexOfEndTag > 0)
+ {
+ text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "");
+ }
+ else
+ {
+ text += "";
+ }
+ }
+ }
+
+ if (text.Contains(@"{\1c")) // "1" specifices primary color
+ {
+ int start = text.IndexOf(@"{\1c", StringComparison.Ordinal);
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal))
+ {
+ string color = text.Substring(start + 5, end - (start + 5));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref color, ref extraTags, out var unknownTags, out italic);
+
+ color = color.RemoveChar('&').TrimStart('H');
+ color = color.PadLeft(6, '0');
+
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLowerInvariant();
+
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ {
+ text = text.Insert(start, "" + unknownTags + "");
+ }
+ else
+ {
+ text = text.Insert(start, "" + unknownTags);
+ }
+
+ text += "";
+ }
+ }
+ }
+ }
+
+ text = text.Replace(@"{\i1}", "");
+ text = text.Replace(@"{\i0}", "");
+ text = text.Replace(@"{\i}", "");
+ if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
+ {
+ text += "";
+ }
+
+ text = text.Replace(@"{\u1}", "");
+ text = text.Replace(@"{\u0}", "");
+ text = text.Replace(@"{\u}", "");
+ if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
+ {
+ text += "";
+ }
+
+ text = text.Replace(@"{\b1}", "");
+ text = text.Replace(@"{\b0}", "");
+ text = text.Replace(@"{\b}", "");
+ if (Utilities.CountTagInText(text, "") > Utilities.CountTagInText(text, ""))
+ {
+ text += "";
+ }
+
+ return text;
+ }
+
+ private static bool ContainsUnsupportedTags(string text)
+ {
+ if (string.IsNullOrEmpty(text) || !text.Contains("{\\", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ var unsupportedTags = new List
+ {
+ "\\alpha",
+ "\\be0",
+ "\\be1",
+ "\\bord",
+ "\\blur",
+ "\\clip",
+ "\\fad",
+ "\\fa",
+ "\\fade",
+ "\\fscx",
+ "\\fscy",
+ "\\fr",
+ "\\iclip",
+ "\\k",
+ "\\K",
+ "\\kf",
+ "\\ko",
+ "\\move",
+ "\\org",
+ "\\p",
+ "\\pos",
+ "\\s0",
+ "\\s1",
+ "\\t(",
+ "\\xbord",
+ "\\ybord",
+ "\\xshad",
+ "\\yshad"
+ };
+
+ foreach (var unsupportedTag in unsupportedTags)
+ {
+ if (text.Contains(unsupportedTag, StringComparison.Ordinal))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out string unknownTags, out bool italic)
+ {
+ italic = false;
+ unknownTags = string.Empty;
+ int indexOfSPlit = tagName.IndexOf('\\');
+ if (indexOfSPlit > 0)
+ {
+ string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
+ tagName = tagName.Remove(indexOfSPlit);
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf('\\');
+ string fontSize = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontSize = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ extraTags += " size=\"" + fontSize.Substring(2) + "\"";
+ }
+ else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf('\\');
+ string fontName = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontName = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ extraTags += " face=\"" + fontName.Substring(2) + "\"";
+ }
+ else if (rest.StartsWith('c') && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf('\\');
+ string fontColor = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontColor = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+
+ string color = fontColor.Substring(2);
+ color = color.RemoveChar('&').TrimStart('H');
+ color = color.PadLeft(6, '0');
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLowerInvariant();
+
+ extraTags += " color=\"" + color + "\"";
+ }
+ else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1)
+ {
+ indexOfSPlit = rest.IndexOf('\\');
+ italic = true;
+ if (indexOfSPlit > 0)
+ {
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ }
+ else if (rest.Length > 0 && rest.Contains("\\"))
+ {
+ indexOfSPlit = rest.IndexOf('\\');
+ var unknowntag = rest.Substring(0, indexOfSPlit);
+ unknownTags += "\\" + unknowntag;
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else if (!string.IsNullOrEmpty(rest))
+ {
+ unknownTags += "\\" + rest;
+ rest = string.Empty;
+ }
+ }
+ }
+ if (!string.IsNullOrEmpty(unknownTags))
+ {
+ unknownTags = "{" + unknownTags + "}";
+ }
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ Errors = null;
+ bool eventsStarted = false;
+ bool fontsStarted = false;
+ bool graphicsStarted = false;
+ subtitle.Paragraphs.Clear();
+
+ // Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text
+ int indexLayer = 0;
+ int indexStart = 1;
+ int indexEnd = 2;
+ int indexStyle = 3;
+ int indexActor = -1; // convert "Actor" to "Nam" (if no "Name")
+ int indexName = 4;
+ int indexMarginL = 5;
+ int indexMarginR = 6;
+ int indexMarginV = 7;
+ int indexEffect = 8;
+ int indexText = 9;
+ var errors = new StringBuilder();
+ int lineNumber = 0;
+
+ var header = new StringBuilder();
+ var footer = new StringBuilder();
+ foreach (string line in lines)
+ {
+ lineNumber++;
+ if (!eventsStarted && !fontsStarted && !graphicsStarted &&
+ !line.Trim().Equals("[fonts]", StringComparison.InvariantCultureIgnoreCase) &&
+ !line.Trim().Equals("[graphics]", StringComparison.InvariantCultureIgnoreCase))
+ {
+ header.AppendLine(line);
+ }
+
+ if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith(';'))
+ {
+ // skip empty and comment lines
+ }
+ else if (line.TrimStart().StartsWith("dialog:", StringComparison.OrdinalIgnoreCase) || line.TrimStart().StartsWith("dialogue:", StringComparison.OrdinalIgnoreCase)) // fix faulty font tags...
+ {
+ eventsStarted = true;
+ fontsStarted = false;
+ graphicsStarted = false;
+ }
+
+ if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase))
+ {
+ if (header.ToString().IndexOf(Environment.NewLine + "[events]", StringComparison.OrdinalIgnoreCase) < 0)
+ {
+ var h = header.ToString().TrimEnd();
+ header.Clear();
+ header.AppendLine(h);
+ header.AppendLine();
+ header.AppendLine("[Events]");
+ }
+ eventsStarted = true;
+ fontsStarted = false;
+ graphicsStarted = false;
+ }
+ else if (line.Trim().Equals("[fonts]", StringComparison.OrdinalIgnoreCase))
+ {
+ eventsStarted = false;
+ fontsStarted = true;
+ graphicsStarted = false;
+ footer.AppendLine();
+ footer.AppendLine("[Fonts]");
+ }
+ else if (line.Trim().Equals("[graphics]", StringComparison.OrdinalIgnoreCase))
+ {
+ eventsStarted = false;
+ fontsStarted = false;
+ graphicsStarted = true;
+ footer.AppendLine();
+ footer.AppendLine("[Graphics]");
+ }
+ else if (line.Trim().Equals("[Aegisub Extradata]", StringComparison.OrdinalIgnoreCase))
+ {
+ eventsStarted = false;
+ fontsStarted = false;
+ graphicsStarted = true;
+ footer.AppendLine();
+ footer.AppendLine("[Aegisub Extradata]");
+ }
+ else if (fontsStarted)
+ {
+ footer.AppendLine(line);
+ }
+ else if (graphicsStarted)
+ {
+ footer.AppendLine(line);
+ }
+ else if (eventsStarted)
+ {
+ string s = line.Trim().ToLowerInvariant();
+ if (line.Length > 10 && s.StartsWith("format:", StringComparison.Ordinal))
+ {
+ indexLayer = -1;
+ indexStart = -1;
+ indexEnd = -1;
+ indexStyle = -1;
+ indexActor = -1;
+ indexName = -1;
+ indexMarginL = -1;
+ indexMarginR = -1;
+ indexMarginV = -1;
+ indexEffect = -1;
+ indexText = -1;
+
+ var format = s.Substring(8).Split(',');
+ for (int i = 0; i < format.Length; i++)
+ {
+ var formatTrimmed = format[i].Trim();
+ if (formatTrimmed.Equals("start", StringComparison.Ordinal))
+ {
+ indexStart = i;
+ }
+ else if (formatTrimmed.Equals("end", StringComparison.Ordinal))
+ {
+ indexEnd = i;
+ }
+ else if (formatTrimmed.Equals("text", StringComparison.Ordinal))
+ {
+ indexText = i;
+ }
+ else if (formatTrimmed.Equals("style", StringComparison.Ordinal))
+ {
+ indexStyle = i;
+ }
+ else if (formatTrimmed.Equals("actor", StringComparison.Ordinal))
+ {
+ indexActor = i;
+ }
+ else if (formatTrimmed.Equals("name", StringComparison.Ordinal))
+ {
+ indexName = i;
+ }
+ else if (formatTrimmed.Equals("marginl", StringComparison.Ordinal))
+ {
+ indexMarginL = i;
+ }
+ else if (formatTrimmed.Equals("marginr", StringComparison.Ordinal))
+ {
+ indexMarginR = i;
+ }
+ else if (formatTrimmed.Equals("marginv", StringComparison.Ordinal))
+ {
+ indexMarginV = i;
+ }
+ else if (formatTrimmed.Equals("effect", StringComparison.Ordinal))
+ {
+ indexEffect = i;
+ }
+ else if (formatTrimmed.Equals("layer", StringComparison.Ordinal))
+ {
+ indexLayer = i;
+ }
+ }
+ }
+ else if (!string.IsNullOrEmpty(s))
+ {
+ var text = string.Empty;
+ var start = string.Empty;
+ var end = string.Empty;
+ var style = string.Empty;
+ var actor = string.Empty;
+ var marginL = string.Empty;
+ var marginR = string.Empty;
+ var marginV = string.Empty;
+ var effect = string.Empty;
+ var layer = 0;
+
+ string[] splittedLine;
+ if (s.StartsWith("dialog:", StringComparison.Ordinal))
+ {
+ splittedLine = line.Remove(0, 7).Split(',');
+ }
+ else if (s.StartsWith("dialogue:", StringComparison.Ordinal))
+ {
+ splittedLine = line.Remove(0, 9).Split(',');
+ }
+ else
+ {
+ splittedLine = line.Split(',');
+ }
+
+ for (int i = 0; i < splittedLine.Length; i++)
+ {
+ if (i == indexStart)
+ {
+ start = splittedLine[i].Trim();
+ }
+ else if (i == indexEnd)
+ {
+ end = splittedLine[i].Trim();
+ }
+ else if (i == indexStyle)
+ {
+ style = splittedLine[i].Trim();
+ }
+ else if (i == indexActor && indexName == -1)
+ {
+ actor = splittedLine[i].Trim();
+ }
+ else if (i == indexName)
+ {
+ actor = splittedLine[i].Trim();
+ }
+ else if (i == indexMarginL)
+ {
+ marginL = splittedLine[i].Trim();
+ }
+ else if (i == indexMarginR)
+ {
+ marginR = splittedLine[i].Trim();
+ }
+ else if (i == indexMarginV)
+ {
+ marginV = splittedLine[i].Trim();
+ }
+ else if (i == indexEffect)
+ {
+ effect = splittedLine[i].Trim();
+ }
+ else if (i == indexLayer)
+ {
+ int.TryParse(splittedLine[i].Replace("Comment:", string.Empty).Trim(), out layer);
+ }
+ else if (i == indexText)
+ {
+ text = splittedLine[i];
+ }
+ else if (i > indexText)
+ {
+ text += "," + splittedLine[i];
+ }
+ }
+
+ try
+ {
+ var p = new Paragraph
+ {
+ StartTime = GetTimeCodeFromString(start),
+ EndTime = GetTimeCodeFromString(end),
+ Text = GetFormattedText(text)
+ };
+
+ if (!string.IsNullOrEmpty(style))
+ {
+ p.Extra = style;
+ }
+
+ if (!string.IsNullOrEmpty(actor))
+ {
+ p.Actor = actor;
+ }
+
+ if (!string.IsNullOrEmpty(marginL))
+ {
+ p.MarginL = marginL;
+ }
+
+ if (!string.IsNullOrEmpty(marginR))
+ {
+ p.MarginR = marginR;
+ }
+
+ if (!string.IsNullOrEmpty(marginV))
+ {
+ p.MarginV = marginV;
+ }
+
+ if (!string.IsNullOrEmpty(effect))
+ {
+ p.Effect = effect;
+ }
+
+ p.Layer = layer;
+ p.IsComment = s.StartsWith("comment:", StringComparison.Ordinal);
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ if (errors.Length < 2000)
+ {
+ errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, lineNumber, line));
+ }
+ else if (subtitle.Paragraphs.Count == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (header.Length > 0)
+ {
+ subtitle.Header = header.ToString();
+ }
+
+ if (footer.Length > 0)
+ {
+ subtitle.Footer = footer.ToString().Trim();
+ }
+
+ subtitle.Renumber();
+ Errors = errors.ToString();
+ }
+
+ private static TimeCode GetTimeCodeFromString(string time)
+ {
+ // h:mm:ss.cc
+ string[] timeCode = time.Split(':', '.');
+ return new TimeCode(int.Parse(timeCode[0]),
+ int.Parse(timeCode[1]),
+ int.Parse(timeCode[2]),
+ int.Parse(timeCode[3]) * 10);
+ }
+
+ public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat)
+ {
+ if (newFormat != null && newFormat.Name == SubStationAlpha.NameOfFormat)
+ {
+ foreach (var p in subtitle.Paragraphs)
+ {
+ string s = p.Text;
+
+ if (s.Contains('{') && s.Contains('}'))
+ {
+ var p1Index = s.IndexOf("\\p1", StringComparison.Ordinal);
+ var p0Index = s.IndexOf("{\\p0}", StringComparison.Ordinal);
+ if (p1Index > 0 && (p0Index > p1Index || p0Index == -1))
+ {
+ var startTagIndex = s.Substring(0, p1Index).LastIndexOf('{');
+ if (startTagIndex >= 0)
+ {
+ if (p0Index > p1Index)
+ {
+ s = s.Remove(startTagIndex, p0Index - startTagIndex + "{\\p0}".Length);
+ }
+ else
+ {
+ s = s.Remove(startTagIndex);
+ }
+ }
+ }
+
+ var karaokeStart = s.IndexOf("{Kara Effector", StringComparison.Ordinal);
+ if (karaokeStart >= 0)
+ {
+ int l = s.IndexOf('}', karaokeStart + 1);
+ if (l < karaokeStart)
+ {
+ break;
+ }
+
+ s = s.Remove(karaokeStart, l - karaokeStart + 1);
+ }
+
+ s = s.Replace(@"\u0", string.Empty);
+ s = s.Replace(@"\u1", string.Empty);
+ s = s.Replace(@"\s0", string.Empty);
+ s = s.Replace(@"\s1", string.Empty);
+ s = s.Replace(@"\be0", string.Empty);
+ s = s.Replace(@"\be1", string.Empty);
+
+ s = RemoveTag(s, "shad");
+ s = RemoveTag(s, "fsc");
+ s = RemoveTag(s, "fsp");
+ s = RemoveTag(s, "fr");
+
+ s = RemoveTag(s, "t(");
+ s = RemoveTag(s, "move(");
+ s = RemoveTag(s, "Position(");
+ s = RemoveTag(s, "org(");
+ s = RemoveTag(s, "fade(");
+ s = RemoveTag(s, "fad(");
+ s = RemoveTag(s, "clip(");
+ s = RemoveTag(s, "iclip(");
+ s = RemoveTag(s, "pbo(");
+ s = RemoveTag(s, "bord");
+ s = RemoveTag(s, "pos");
+
+ // TODO: Alignment tags
+
+ s = s.Replace("{}", string.Empty);
+
+ p.Text = s;
+ }
+ }
+ }
+ else
+ {
+ foreach (var p in subtitle.Paragraphs)
+ {
+ var noTags = Utilities.RemoveSsaTags(p.Text).Trim();
+ if (noTags.Length == 0)
+ {
+ p.Text = string.Empty;
+ continue;
+ }
+
+ p.Text = p.Text.Replace("\\n", Environment.NewLine); // Soft line break
+ p.Text = p.Text.Replace("\\N", Environment.NewLine); // Hard line break
+ p.Text = p.Text.Replace("\\h", " "); // Hard space
+
+ if (noTags.StartsWith("m ", StringComparison.Ordinal))
+ {
+ var test = noTags.Remove(0, 2)
+ .RemoveChar('0')
+ .RemoveChar('1')
+ .RemoveChar('2')
+ .RemoveChar('3')
+ .RemoveChar('4')
+ .RemoveChar('5')
+ .RemoveChar('6')
+ .RemoveChar('7')
+ .RemoveChar('8')
+ .RemoveChar('9')
+ .RemoveChar('-')
+ .RemoveChar('l')
+ .RemoveChar('m')
+ .RemoveChar(' ')
+ .RemoveChar('.');
+ if (test.Length == 0)
+ {
+ p.Text = string.Empty;
+ continue;
+ }
+ }
+
+ int indexOfBegin = p.Text.IndexOf('{');
+ string pre = string.Empty;
+ while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin)
+ {
+ string s = p.Text.Substring(indexOfBegin);
+ if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an2}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an3}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an4}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an5}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an6}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an7}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an8}", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ pre = s.Substring(0, 6);
+ }
+ else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an2\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an3\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an4\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an5\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an6\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an7\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an8\\", StringComparison.Ordinal) ||
+ s.StartsWith("{\\an9\\", StringComparison.Ordinal))
+ {
+ pre = s.Substring(0, 5) + "}";
+ }
+ int indexOfEnd = p.Text.IndexOf('}');
+ p.Text = p.Text.Remove(indexOfBegin, indexOfEnd - indexOfBegin + 1);
+
+ indexOfBegin = p.Text.IndexOf('{');
+ }
+ p.Text = pre + p.Text;
+ }
+ }
+ }
+
+ private static string RemoveTag(string s, string tag)
+ {
+ int indexOfTag = s.IndexOf(@"\" + tag, StringComparison.Ordinal);
+ if (indexOfTag > 0)
+ {
+ var endIndex1 = s.IndexOf('\\', indexOfTag + 1);
+ var endIndex2 = s.IndexOf('}', indexOfTag + 1);
+ endIndex1 = Math.Min(endIndex1, endIndex2);
+ if (endIndex1 > 0)
+ {
+ return s.Remove(indexOfTag, endIndex1 - indexOfTag);
+ }
+ }
+ return s;
+ }
+
+ ///
+ /// BGR color like this: &HBBGGRR& (where BB, GG, and RR are hex values in uppercase)
+ ///
+ /// Input string
+ /// Default color
+ /// Input string as color, or default color if problems
+ public static Color GetSsaColor(string f, Color defaultColor)
+ {
+ //Red = &H0000FF&
+ //Green = &H00FF00&
+ //Blue = &HFF0000&
+ //White = &HFFFFFF&
+ //Black = &H000000&
+ string s = f.Trim().Trim('&');
+
+ if (s.StartsWith('h') && s.Length < 7)
+ {
+ while (s.Length < 7)
+ {
+ s = s.Insert(1, "0");
+ }
+ }
+
+ if (s.StartsWith('h') && s.Length == 7)
+ {
+ s = s.Substring(1);
+ string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
+ try
+ {
+ return ColorTranslator.FromHtml(hexColor);
+ }
+ catch
+ {
+ return defaultColor;
+ }
+ }
+ if (s.StartsWith('h') && s.Length == 9)
+ {
+ if (int.TryParse(s.Substring(1, 2), NumberStyles.HexNumber, null, out var alpha))
+ {
+ alpha = 255 - alpha; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
+ }
+ else
+ {
+ alpha = 255; // full color
+ }
+ s = s.Substring(3);
+ string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
+ try
+ {
+ var c = ColorTranslator.FromHtml(hexColor);
+ return Color.FromArgb(alpha, c);
+ }
+ catch
+ {
+ return defaultColor;
+ }
+ }
+
+ if (int.TryParse(f, out var number))
+ {
+ var temp = Color.FromArgb(number);
+ return Color.FromArgb(255, temp.B, temp.G, temp.R);
+ }
+ return defaultColor;
+ }
+
+ public static string GetSsaColorString(Color c)
+ {
+ return $"&H{255 - c.A:X2}{c.B:X2}{c.G:X2}{c.R:X2}"; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
+ }
+
+ public static string CheckForErrors(string header)
+ {
+ if (string.IsNullOrEmpty(header))
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder();
+
+ int styleCount = -1;
+
+ int nameIndex = -1;
+ int fontNameIndex = -1;
+ int fontsizeIndex = -1;
+ int primaryColourIndex = -1;
+ int secondaryColourIndex = -1;
+ int outlineColourIndex = -1;
+ int backColourIndex = -1;
+ int boldIndex = -1;
+ int italicIndex = -1;
+ int underlineIndex = -1;
+ int outlineIndex = -1;
+ int shadowIndex = -1;
+ int alignmentIndex = -1;
+ int marginLIndex = -1;
+ int marginRIndex = -1;
+ int marginVIndex = -1;
+ int borderStyleIndex = -1;
+
+ foreach (string line in header.SplitToLines())
+ {
+ string s = line.Trim().ToLowerInvariant();
+ if (s.StartsWith("format:", StringComparison.Ordinal))
+ {
+ if (line.Length > 10)
+ {
+ var format = line.Substring(8).ToLowerInvariant().Split(',');
+ styleCount = format.Length;
+ for (int i = 0; i < format.Length; i++)
+ {
+ string f = format[i].Trim();
+ if (f == "name")
+ {
+ nameIndex = i;
+ }
+ else if (f == "fontname")
+ {
+ fontNameIndex = i;
+ }
+ else if (f == "fontsize")
+ {
+ fontsizeIndex = i;
+ }
+ else if (f == "primarycolour")
+ {
+ primaryColourIndex = i;
+ }
+ else if (f == "secondarycolour")
+ {
+ secondaryColourIndex = i;
+ }
+ else if (f == "outlinecolour")
+ {
+ outlineColourIndex = i;
+ }
+ else if (f == "backcolour")
+ {
+ backColourIndex = i;
+ }
+ else if (f == "bold")
+ {
+ boldIndex = i;
+ }
+ else if (f == "italic")
+ {
+ italicIndex = i;
+ }
+ else if (f == "underline")
+ {
+ underlineIndex = i;
+ }
+ else if (f == "outline")
+ {
+ outlineIndex = i;
+ }
+ else if (f == "shadow")
+ {
+ shadowIndex = i;
+ }
+ else if (f == "alignment")
+ {
+ alignmentIndex = i;
+ }
+ else if (f == "marginl")
+ {
+ marginLIndex = i;
+ }
+ else if (f == "marginr")
+ {
+ marginRIndex = i;
+ }
+ else if (f == "marginv")
+ {
+ marginVIndex = i;
+ }
+ else if (f == "borderstyle")
+ {
+ borderStyleIndex = i;
+ }
+ }
+ }
+ }
+ else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
+ {
+ if (line.Length > 10)
+ {
+ string rawLine = line;
+ var format = line.Substring(6).Split(',');
+
+ if (format.Length != styleCount)
+ {
+ sb.AppendLine("Number of expected Style elements do not match number of Format elements: " + rawLine);
+ sb.AppendLine();
+ }
+ else
+ {
+ var dummyColor = Color.FromArgb(9, 14, 16, 26);
+ for (int i = 0; i < format.Length; i++)
+ {
+ string f = format[i].Trim().ToLowerInvariant();
+ if (i == nameIndex)
+ {
+ if (f.Length == 0)
+ {
+ sb.AppendLine("'Name' is empty: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == fontNameIndex)
+ {
+ if (f.Length == 0)
+ {
+ sb.AppendLine("'Fontname' is empty: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == fontsizeIndex)
+ {
+ if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'Fontsize' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == primaryColourIndex)
+ {
+ if (GetSsaColor(f, dummyColor) == dummyColor || f == "&h")
+ {
+ sb.AppendLine("'PrimaryColour' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == secondaryColourIndex)
+ {
+ if (GetSsaColor(f, dummyColor) == dummyColor)
+ {
+ sb.AppendLine("'SecondaryColour' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == outlineColourIndex)
+ {
+ if (GetSsaColor(f, dummyColor) == dummyColor)
+ {
+ sb.AppendLine("'OutlineColour' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == backColourIndex)
+ {
+ if (GetSsaColor(f, dummyColor) == dummyColor)
+ {
+ sb.AppendLine("'BackColour' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == boldIndex)
+ {
+ if (Utilities.AllLetters.Contains(f))
+ {
+ sb.AppendLine("'Bold' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == italicIndex)
+ {
+ if (Utilities.AllLetters.Contains(f))
+ {
+ sb.AppendLine("'Italic' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == underlineIndex)
+ {
+ if (Utilities.AllLetters.Contains(f))
+ {
+ sb.AppendLine("'Underline' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == outlineIndex)
+ {
+ if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'Outline' (width) incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == shadowIndex)
+ {
+ if (!float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'Shadow' (width) incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == alignmentIndex)
+ {
+ if (!"101123456789 ".Contains(f))
+ {
+ sb.AppendLine("'Alignment' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == marginLIndex)
+ {
+ if (!int.TryParse(f, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'MarginL' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == marginRIndex)
+ {
+ if (!int.TryParse(f, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'MarginR' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == marginVIndex)
+ {
+ if (!int.TryParse(f, out _) || f.StartsWith('-'))
+ {
+ sb.AppendLine("'MarginV' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ else if (i == borderStyleIndex)
+ {
+ if (f.Length != 0 && !"123".Contains(f))
+ {
+ sb.AppendLine("'BorderStyle' incorrect: " + rawLine);
+ sb.AppendLine();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return sb.ToString();
+ }
+
+ ///
+ /// Add new style to ASS header
+ ///
+ /// Header with new style
+ public static string AddSsaStyle(SsaStyle style, string inputHeader)
+ {
+ var header = inputHeader;
+ if (string.IsNullOrEmpty(header))
+ {
+ header = DefaultHeader;
+ }
+
+ var sb = new StringBuilder();
+ bool stylesStarted = false;
+ bool styleAdded = false;
+ string styleFormat = "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding";
+ foreach (string line in header.SplitToLines())
+ {
+ if (line.Equals("[V4+ Styles]", StringComparison.OrdinalIgnoreCase) || line.Equals("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
+ {
+ stylesStarted = true;
+ }
+
+ if (line.StartsWith("format:", StringComparison.OrdinalIgnoreCase))
+ {
+ styleFormat = line;
+ }
+
+ if (!line.StartsWith("Style: " + style.Name + ",", StringComparison.Ordinal)) // overwrite existing style
+ {
+ sb.AppendLine(line);
+ }
+
+ if (!styleAdded && stylesStarted && line.TrimStart().StartsWith("style:", StringComparison.OrdinalIgnoreCase))
+ {
+ sb.AppendLine(style.ToRawAss(styleFormat));
+ styleAdded = true;
+ }
+ }
+ return sb.ToString();
+ }
+
+ public static SsaStyle GetSsaStyle(string styleName, string header)
+ {
+ var style = new SsaStyle { Name = styleName };
+
+ int nameIndex = -1;
+ int fontNameIndex = -1;
+ int fontsizeIndex = -1;
+ int primaryColourIndex = -1;
+ int secondaryColourIndex = -1;
+ int tertiaryColourIndex = -1;
+ int outlineColourIndex = -1;
+ int backColourIndex = -1;
+ int boldIndex = -1;
+ int italicIndex = -1;
+ int underlineIndex = -1;
+ int outlineIndex = -1;
+ int shadowIndex = -1;
+ int alignmentIndex = -1;
+ int marginLIndex = -1;
+ int marginRIndex = -1;
+ int marginVIndex = -1;
+ int borderStyleIndex = -1;
+
+ if (header == null)
+ {
+ header = DefaultHeader;
+ }
+
+ foreach (string line in header.SplitToLines())
+ {
+ string s = line.Trim().ToLowerInvariant();
+ if (s.StartsWith("format:", StringComparison.Ordinal))
+ {
+ if (line.Length > 10)
+ {
+ var format = line.ToLowerInvariant().Substring(8).Split(',');
+ for (int i = 0; i < format.Length; i++)
+ {
+ string f = format[i].Trim().ToLowerInvariant();
+ if (f == "name")
+ {
+ nameIndex = i;
+ }
+ else if (f == "fontname")
+ {
+ fontNameIndex = i;
+ }
+ else if (f == "fontsize")
+ {
+ fontsizeIndex = i;
+ }
+ else if (f == "primarycolour")
+ {
+ primaryColourIndex = i;
+ }
+ else if (f == "secondarycolour")
+ {
+ secondaryColourIndex = i;
+ }
+ else if (f == "tertiarycolour")
+ {
+ tertiaryColourIndex = i;
+ }
+ else if (f == "outlinecolour")
+ {
+ outlineColourIndex = i;
+ }
+ else if (f == "backcolour")
+ {
+ backColourIndex = i;
+ }
+ else if (f == "bold")
+ {
+ boldIndex = i;
+ }
+ else if (f == "italic")
+ {
+ italicIndex = i;
+ }
+ else if (f == "underline")
+ {
+ underlineIndex = i;
+ }
+ else if (f == "outline")
+ {
+ outlineIndex = i;
+ }
+ else if (f == "shadow")
+ {
+ shadowIndex = i;
+ }
+ else if (f == "alignment")
+ {
+ alignmentIndex = i;
+ }
+ else if (f == "marginl")
+ {
+ marginLIndex = i;
+ }
+ else if (f == "marginr")
+ {
+ marginRIndex = i;
+ }
+ else if (f == "marginv")
+ {
+ marginVIndex = i;
+ }
+ else if (f == "borderstyle")
+ {
+ borderStyleIndex = i;
+ }
+ }
+ }
+ }
+ else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
+ {
+ if (line.Length > 10)
+ {
+ style.RawLine = line;
+ var format = line.Substring(6).Split(',');
+ for (int i = 0; i < format.Length; i++)
+ {
+ string f = format[i].Trim().ToLowerInvariant();
+ if (i == nameIndex)
+ {
+ style.Name = format[i].Trim();
+ }
+ else if (i == fontNameIndex)
+ {
+ style.FontName = f;
+ }
+ else if (i == fontsizeIndex)
+ {
+ if (float.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var fOut))
+ {
+ style.FontSize = fOut;
+ }
+ }
+ else if (i == primaryColourIndex)
+ {
+ style.Primary = GetSsaColor(f, Color.White);
+ }
+ else if (i == secondaryColourIndex)
+ {
+ style.Secondary = GetSsaColor(f, Color.Yellow);
+ }
+ else if (i == tertiaryColourIndex)
+ {
+ style.Tertiary = GetSsaColor(f, Color.Yellow);
+ }
+ else if (i == outlineColourIndex)
+ {
+ style.Outline = GetSsaColor(f, Color.Black);
+ }
+ else if (i == backColourIndex)
+ {
+ style.Background = GetSsaColor(f, Color.Black);
+ }
+ else if (i == boldIndex)
+ {
+ style.Bold = f == "-1" || f == "1";
+ }
+ else if (i == italicIndex)
+ {
+ style.Italic = f == "-1" || f == "1";
+ }
+ else if (i == underlineIndex)
+ {
+ style.Underline = f == "-1" || f == "1";
+ }
+ else if (i == outlineIndex)
+ {
+ if (decimal.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var number))
+ {
+ style.OutlineWidth = number;
+ }
+ }
+ else if (i == shadowIndex)
+ {
+ if (decimal.TryParse(f, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var number))
+ {
+ style.ShadowWidth = number;
+ }
+ }
+ else if (i == alignmentIndex)
+ {
+ style.Alignment = f;
+ }
+ else if (i == marginLIndex)
+ {
+ if (int.TryParse(f, out var number))
+ {
+ style.MarginLeft = number;
+ }
+ }
+ else if (i == marginRIndex)
+ {
+ if (int.TryParse(f, out var number))
+ {
+ style.MarginRight = number;
+ }
+ }
+ else if (i == marginVIndex)
+ {
+ if (int.TryParse(f, out var number))
+ {
+ style.MarginVertical = number;
+ }
+ }
+ else if (i == borderStyleIndex)
+ {
+ style.BorderStyle = f;
+ }
+ }
+ }
+ if (styleName != null && style.Name != null && (styleName.Equals(style.Name, StringComparison.OrdinalIgnoreCase) ||
+ styleName.Equals("*Default", StringComparison.OrdinalIgnoreCase) &&
+ style.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)))
+ {
+ style.LoadedFromHeader = true;
+ return style;
+ }
+ }
+ }
+ return new SsaStyle { Name = styleName };
+ }
+
+ public override bool HasStyleSupport => true;
+ }
}
\ No newline at end of file
diff --git a/libse/SubtitleFormats/AribB24Decoder.cs b/src/libse/SubtitleFormats/AribB24Decoder.cs
similarity index 100%
rename from libse/SubtitleFormats/AribB24Decoder.cs
rename to src/libse/SubtitleFormats/AribB24Decoder.cs
diff --git a/libse/SubtitleFormats/AribB36.cs b/src/libse/SubtitleFormats/AribB36.cs
similarity index 100%
rename from libse/SubtitleFormats/AribB36.cs
rename to src/libse/SubtitleFormats/AribB36.cs
diff --git a/libse/SubtitleFormats/AvidCaption.cs b/src/libse/SubtitleFormats/AvidCaption.cs
similarity index 97%
rename from libse/SubtitleFormats/AvidCaption.cs
rename to src/libse/SubtitleFormats/AvidCaption.cs
index 24478613d..877f74463 100644
--- a/libse/SubtitleFormats/AvidCaption.cs
+++ b/src/libse/SubtitleFormats/AvidCaption.cs
@@ -1,127 +1,127 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class AvidCaption : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "Avid Caption";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var sb = new StringBuilder();
- sb.AppendLine("@ This file written with the Avid Caption plugin, version 1");
- sb.AppendLine();
- sb.AppendLine("");
- const string writeFormat = "{0} {1}{2}{3}{2}";
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendLine(string.Format(writeFormat, p.StartTime.ToHHMMSSFF(), EncodeEndTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text, true)));
- //00:50:34:22 00:50:39:13
- //Ich muss dafür sorgen,
- //dass die Epsteins weiterleben
- }
- sb.AppendLine("");
- return sb.ToString();
- }
-
- private static string EncodeEndTimeCode(TimeCode time)
- {
- //00:50:39:13 (last is frame)
-
- //Bugfix for Avid - On 23.976 FPS and 24 FPS projects, when the End time of a subtitle ends in 02, 07, 12, 17, 22, 27 frames, the subtitle won't import.
- if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 23.976) < 0.01 ||
- Math.Abs(Configuration.Settings.General.CurrentFrameRate - 24) < 0.01)
- {
- var frames = MillisecondsToFramesMaxFrameRate(time.Milliseconds);
- if (frames == 2 || frames == 7 || frames == 12 || frames == 17 || frames == 22 || frames == 27)
- {
- frames--;
- }
-
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{frames:00}";
- }
- else
- {
- return time.ToHHMMSSFF();
- }
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- //00:03:15:22 00:03:23:10 This is line one.
- //This is line two.
- Paragraph p = null;
- subtitle.Paragraphs.Clear();
- _errorCount = 0;
- bool beginFound = false;
- bool endFound = false;
- foreach (string line in lines)
- {
- string tline = line.Trim();
- if (tline.Equals("", StringComparison.OrdinalIgnoreCase))
- {
- beginFound = true;
- }
- else if (tline.Equals("", StringComparison.OrdinalIgnoreCase))
- {
- endFound = true;
- break;
- }
-
- if (line.IndexOf(':') == 2 && RegexTimeCodes.IsMatch(line))
- {
- string temp = line.Substring(0, RegexTimeCodes.Match(line).Length);
- string start = temp.Substring(0, 11);
- string end = temp.Substring(12, 11);
-
- string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- if (startParts.Length == 4 && endParts.Length == 4)
- {
- p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), string.Empty);
- subtitle.Paragraphs.Add(p);
- }
- }
- else if (tline.Length == 0 || tline[0] == '@')
- {
- // skip these lines
- }
- else if (tline.Length > 0 && p != null)
- {
- if (string.IsNullOrEmpty(p.Text))
- {
- p.Text = line;
- }
- else
- {
- if (Utilities.IsInteger(line))
- {
- _errorCount++;
- }
- p.Text = p.Text.TrimEnd() + Environment.NewLine + line;
- }
- }
- }
- if (!beginFound)
- {
- _errorCount++;
- }
-
- if (!endFound)
- {
- _errorCount++;
- }
-
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class AvidCaption : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "Avid Caption";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("@ This file written with the Avid Caption plugin, version 1");
+ sb.AppendLine();
+ sb.AppendLine("");
+ const string writeFormat = "{0} {1}{2}{3}{2}";
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendLine(string.Format(writeFormat, p.StartTime.ToHHMMSSFF(), EncodeEndTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text, true)));
+ //00:50:34:22 00:50:39:13
+ //Ich muss dafür sorgen,
+ //dass die Epsteins weiterleben
+ }
+ sb.AppendLine("");
+ return sb.ToString();
+ }
+
+ private static string EncodeEndTimeCode(TimeCode time)
+ {
+ //00:50:39:13 (last is frame)
+
+ //Bugfix for Avid - On 23.976 FPS and 24 FPS projects, when the End time of a subtitle ends in 02, 07, 12, 17, 22, 27 frames, the subtitle won't import.
+ if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 23.976) < 0.01 ||
+ Math.Abs(Configuration.Settings.General.CurrentFrameRate - 24) < 0.01)
+ {
+ var frames = MillisecondsToFramesMaxFrameRate(time.Milliseconds);
+ if (frames == 2 || frames == 7 || frames == 12 || frames == 17 || frames == 22 || frames == 27)
+ {
+ frames--;
+ }
+
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{frames:00}";
+ }
+ else
+ {
+ return time.ToHHMMSSFF();
+ }
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ //00:03:15:22 00:03:23:10 This is line one.
+ //This is line two.
+ Paragraph p = null;
+ subtitle.Paragraphs.Clear();
+ _errorCount = 0;
+ bool beginFound = false;
+ bool endFound = false;
+ foreach (string line in lines)
+ {
+ string tline = line.Trim();
+ if (tline.Equals("", StringComparison.OrdinalIgnoreCase))
+ {
+ beginFound = true;
+ }
+ else if (tline.Equals("", StringComparison.OrdinalIgnoreCase))
+ {
+ endFound = true;
+ break;
+ }
+
+ if (line.IndexOf(':') == 2 && RegexTimeCodes.IsMatch(line))
+ {
+ string temp = line.Substring(0, RegexTimeCodes.Match(line).Length);
+ string start = temp.Substring(0, 11);
+ string end = temp.Substring(12, 11);
+
+ string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ if (startParts.Length == 4 && endParts.Length == 4)
+ {
+ p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), string.Empty);
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ else if (tline.Length == 0 || tline[0] == '@')
+ {
+ // skip these lines
+ }
+ else if (tline.Length > 0 && p != null)
+ {
+ if (string.IsNullOrEmpty(p.Text))
+ {
+ p.Text = line;
+ }
+ else
+ {
+ if (Utilities.IsInteger(line))
+ {
+ _errorCount++;
+ }
+ p.Text = p.Text.TrimEnd() + Environment.NewLine + line;
+ }
+ }
+ }
+ if (!beginFound)
+ {
+ _errorCount++;
+ }
+
+ if (!endFound)
+ {
+ _errorCount++;
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/AvidDvd.cs b/src/libse/SubtitleFormats/AvidDvd.cs
similarity index 97%
rename from libse/SubtitleFormats/AvidDvd.cs
rename to src/libse/SubtitleFormats/AvidDvd.cs
index b053c51f5..87e455d48 100644
--- a/libse/SubtitleFormats/AvidDvd.cs
+++ b/src/libse/SubtitleFormats/AvidDvd.cs
@@ -1,155 +1,155 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class AvidDvd : SubtitleFormat
- {
- //25 10:03:20:23 10:03:23:05 some text
- //I see, on my way.|New line also.
- //
- //26 10:03:31:18 10:03:34:00 even more text
- //Panessa, why didn't they give them
- //an escape route ?
-
- private static readonly Regex RegexTimeCode = new Regex(@"^\d+\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t.+$", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "Avid DVD";
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName != null)
- {
- if (fileName.EndsWith(".dost", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (fileName.EndsWith(".sst", StringComparison.OrdinalIgnoreCase) && new SonicScenaristBitmaps().IsMine(lines, fileName))
- {
- return false;
- }
- }
-
- return base.IsMine(lines, fileName);
- }
-
- private static string MakeTimeCode(TimeCode tc)
- {
- return tc.ToHHMMSSFF();
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var sb = new StringBuilder();
- int count = 1;
- bool italic = false;
- for (int i = 0; i < subtitle.Paragraphs.Count; i++)
- {
- Paragraph p = subtitle.Paragraphs[i];
- string text = p.Text;
- if (text.StartsWith('{') && text.Length > 6 && text[6] == '}')
- {
- text = text.Remove(0, 6);
- }
-
- if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal))
- {
- if (!italic)
- {
- italic = true;
- sb.AppendLine("$Italic = TRUE");
- }
- }
- else if (italic)
- {
- italic = false;
- sb.AppendLine("$Italic = FALSE");
- }
-
- text = HtmlUtil.RemoveHtmlTags(text, true);
- sb.AppendLine($"{count}\t{MakeTimeCode(p.StartTime)}\t{MakeTimeCode(p.EndTime)}\t{text.Replace(Environment.NewLine, "|")}");
- sb.AppendLine();
- count++;
- }
-
- return sb.ToString();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- Paragraph p = null;
- var sb = new StringBuilder();
- bool italic = false;
- foreach (string line in lines)
- {
- string s = line.TrimEnd();
- if (RegexTimeCode.IsMatch(s))
- {
- try
- {
- if (p != null)
- {
- p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim();
- subtitle.Paragraphs.Add(p);
- }
- sb.Clear();
- string[] arr = s.Split('\t');
- if (arr.Length >= 3)
- {
- string text = s.Remove(0, arr[0].Length + arr[1].Length + arr[2].Length + 2).Trim();
-
- if (string.IsNullOrWhiteSpace(text.Replace("0", string.Empty).Replace("1", string.Empty).Replace("2", string.Empty).Replace("3", string.Empty).Replace("4", string.Empty).Replace("5", string.Empty).
- Replace("6", string.Empty).Replace("7", string.Empty).Replace("8", string.Empty).Replace("9", string.Empty).RemoveChar('.').RemoveChar(':').RemoveChar(',')))
- {
- _errorCount++;
- }
-
- if (italic)
- {
- text = "" + text + "";
- }
-
- sb.AppendLine(text);
- char[] splitChars = { ',', '.', ':' };
- p = new Paragraph(DecodeTimeCodeFrames(arr[1], splitChars), DecodeTimeCodeFrames(arr[2], splitChars), string.Empty);
- }
- }
- catch
- {
- _errorCount++;
- p = null;
- }
- }
- else if (s.StartsWith('$'))
- {
- if (s.RemoveChar(' ').Equals("$italic=true", StringComparison.OrdinalIgnoreCase))
- {
- italic = true;
- }
- else if (s.RemoveChar(' ').Equals("$italic=false", StringComparison.OrdinalIgnoreCase))
- {
- italic = false;
- }
- }
- else if (!string.IsNullOrWhiteSpace(s))
- {
- sb.AppendLine(s);
- }
- }
- if (p != null)
- {
- p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim();
- subtitle.Paragraphs.Add(p);
- }
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class AvidDvd : SubtitleFormat
+ {
+ //25 10:03:20:23 10:03:23:05 some text
+ //I see, on my way.|New line also.
+ //
+ //26 10:03:31:18 10:03:34:00 even more text
+ //Panessa, why didn't they give them
+ //an escape route ?
+
+ private static readonly Regex RegexTimeCode = new Regex(@"^\d+\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t.+$", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "Avid DVD";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (fileName != null)
+ {
+ if (fileName.EndsWith(".dost", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (fileName.EndsWith(".sst", StringComparison.OrdinalIgnoreCase) && new SonicScenaristBitmaps().IsMine(lines, fileName))
+ {
+ return false;
+ }
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ private static string MakeTimeCode(TimeCode tc)
+ {
+ return tc.ToHHMMSSFF();
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var sb = new StringBuilder();
+ int count = 1;
+ bool italic = false;
+ for (int i = 0; i < subtitle.Paragraphs.Count; i++)
+ {
+ Paragraph p = subtitle.Paragraphs[i];
+ string text = p.Text;
+ if (text.StartsWith('{') && text.Length > 6 && text[6] == '}')
+ {
+ text = text.Remove(0, 6);
+ }
+
+ if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal))
+ {
+ if (!italic)
+ {
+ italic = true;
+ sb.AppendLine("$Italic = TRUE");
+ }
+ }
+ else if (italic)
+ {
+ italic = false;
+ sb.AppendLine("$Italic = FALSE");
+ }
+
+ text = HtmlUtil.RemoveHtmlTags(text, true);
+ sb.AppendLine($"{count}\t{MakeTimeCode(p.StartTime)}\t{MakeTimeCode(p.EndTime)}\t{text.Replace(Environment.NewLine, "|")}");
+ sb.AppendLine();
+ count++;
+ }
+
+ return sb.ToString();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ Paragraph p = null;
+ var sb = new StringBuilder();
+ bool italic = false;
+ foreach (string line in lines)
+ {
+ string s = line.TrimEnd();
+ if (RegexTimeCode.IsMatch(s))
+ {
+ try
+ {
+ if (p != null)
+ {
+ p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim();
+ subtitle.Paragraphs.Add(p);
+ }
+ sb.Clear();
+ string[] arr = s.Split('\t');
+ if (arr.Length >= 3)
+ {
+ string text = s.Remove(0, arr[0].Length + arr[1].Length + arr[2].Length + 2).Trim();
+
+ if (string.IsNullOrWhiteSpace(text.Replace("0", string.Empty).Replace("1", string.Empty).Replace("2", string.Empty).Replace("3", string.Empty).Replace("4", string.Empty).Replace("5", string.Empty).
+ Replace("6", string.Empty).Replace("7", string.Empty).Replace("8", string.Empty).Replace("9", string.Empty).RemoveChar('.').RemoveChar(':').RemoveChar(',')))
+ {
+ _errorCount++;
+ }
+
+ if (italic)
+ {
+ text = "" + text + "";
+ }
+
+ sb.AppendLine(text);
+ char[] splitChars = { ',', '.', ':' };
+ p = new Paragraph(DecodeTimeCodeFrames(arr[1], splitChars), DecodeTimeCodeFrames(arr[2], splitChars), string.Empty);
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ p = null;
+ }
+ }
+ else if (s.StartsWith('$'))
+ {
+ if (s.RemoveChar(' ').Equals("$italic=true", StringComparison.OrdinalIgnoreCase))
+ {
+ italic = true;
+ }
+ else if (s.RemoveChar(' ').Equals("$italic=false", StringComparison.OrdinalIgnoreCase))
+ {
+ italic = false;
+ }
+ }
+ else if (!string.IsNullOrWhiteSpace(s))
+ {
+ sb.AppendLine(s);
+ }
+ }
+ if (p != null)
+ {
+ p.Text = sb.ToString().Replace("|", Environment.NewLine).Trim();
+ subtitle.Paragraphs.Add(p);
+ }
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/AvidStl.cs b/src/libse/SubtitleFormats/AvidStl.cs
similarity index 97%
rename from libse/SubtitleFormats/AvidStl.cs
rename to src/libse/SubtitleFormats/AvidStl.cs
index db141dea9..5f97022d5 100644
--- a/libse/SubtitleFormats/AvidStl.cs
+++ b/src/libse/SubtitleFormats/AvidStl.cs
@@ -1,194 +1,194 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class AvidStl : SubtitleFormat
- {
- private const int TextLength = 112;
-
- private static Paragraph ReadSubtitleBlock(byte[] buffer, int index)
- {
- index += 5;
- var p = new Paragraph { StartTime = ReadTimeCode(buffer, ref index), EndTime = ReadTimeCode(buffer, ref index) };
- index += 3;
- for (int i = index; i < index + TextLength; i++)
- {
- if (buffer[i] == 0x8f || buffer[i] == 0)
- {
- buffer[i] = 32;
- }
- else if (buffer[i] == 0x8a)
- {
- buffer[i] = 0xa;
- }
- }
- p.Text = Encoding.GetEncoding(1252).GetString(buffer, index, TextLength).Trim();
- p.Text = p.Text.Replace("\n", Environment.NewLine);
- return p;
- }
-
- private static TimeCode ReadTimeCode(byte[] buffer, ref int index)
- {
- int hours = buffer[index];
- int minutes = buffer[index + 1];
- int seconds = buffer[index + 2];
- int milliseconds = FramesToMillisecondsMax999(buffer[index + 3]);
- index += 4;
- return new TimeCode(hours, minutes, seconds, milliseconds);
- }
-
- public static void WriteSubtitleBlock(FileStream fs, Paragraph p, int number)
- {
- fs.WriteByte(0);
- fs.WriteByte((byte)(number % 256)); // number - low byte
- fs.WriteByte((byte)(number / 256)); // number - high byte
- fs.WriteByte(0xff);
- fs.WriteByte(0);
- WriteTimeCode(fs, p.StartTime);
- WriteTimeCode(fs, p.EndTime);
- fs.WriteByte(1);
- fs.WriteByte(2);
- fs.WriteByte(0);
- var buffer = Encoding.GetEncoding(1252).GetBytes(p.Text.Replace(Environment.NewLine, "Š"));
- if (buffer.Length <= 128)
- {
- fs.Write(buffer, 0, buffer.Length);
- for (int i = buffer.Length; i < TextLength; i++)
- {
- fs.WriteByte(0x8f);
- }
- }
- else
- {
- for (int i = 0; i < TextLength; i++)
- {
- fs.WriteByte(buffer[i]);
- }
- }
- }
-
- private static void WriteTimeCode(FileStream fs, TimeCode tc)
- {
- fs.WriteByte((byte)(tc.Hours));
- fs.WriteByte((byte)(tc.Minutes));
- fs.WriteByte((byte)(tc.Seconds));
- fs.WriteByte((byte)(MillisecondsToFramesMaxFrameRate(tc.Milliseconds)));
- }
-
- public override string Extension => ".stl";
-
- public const string NameOfFormat = "Avid STL";
-
- public override string Name => NameOfFormat;
-
- public static void Save(string fileName, Subtitle subtitle)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- byte[] buffer = { 0x38, 0x35, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30, 0x30, 0x30, 0x39 };
- fs.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 0xde; i++)
- {
- fs.WriteByte(0);
- }
-
- string numberOfLines = subtitle.Paragraphs.Count.ToString("D5");
-
- buffer = Encoding.ASCII.GetBytes(numberOfLines + numberOfLines + "001");
- fs.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 0x15; i++)
- {
- fs.WriteByte(0);
- }
-
- buffer = Encoding.ASCII.GetBytes("11");
- fs.Write(buffer, 0, buffer.Length);
- while (fs.Length < 1024)
- {
- fs.WriteByte(0);
- }
-
- int subtitleNumber = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- WriteSubtitleBlock(fs, p, subtitleNumber);
- subtitleNumber++;
- }
- }
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- try
- {
- var fi = new FileInfo(fileName);
- if (fi.Length > 1150 && fi.Length < 1024000) // not too small or too big
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- if (buffer[0] == 0x38 &&
- buffer[1] == 0x35 &&
- buffer[2] == 0x30 &&
- buffer[1024] == 0 &&
- buffer[1025] == 0 &&
- buffer[1026] == 0 &&
- buffer[1027] == 0xff)
- {
- return true;
- }
-
- if (fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase) &&
- buffer.Length > 1283 &&
- buffer[1024] == 0 &&
- buffer[1025] == 1 &&
- buffer[1026] == 0 &&
- buffer[1027] == 0xff &&
- buffer[1152] == 0 &&
- buffer[1153] == 2 &&
- buffer[1154] == 0 &&
- buffer[1155] == 0xff &&
- buffer[1280] == 0 &&
- buffer[1281] == 3 &&
- buffer[1282] == 0 &&
- buffer[1283] == 0xff)
- {
- return true;
- }
- }
- }
- catch
- {
- return false;
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- int index = 1024;
- while (index <= buffer.Length - 128)
- {
- Paragraph p = ReadSubtitleBlock(buffer, index);
- subtitle.Paragraphs.Add(p);
- index += 128;
- }
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class AvidStl : SubtitleFormat
+ {
+ private const int TextLength = 112;
+
+ private static Paragraph ReadSubtitleBlock(byte[] buffer, int index)
+ {
+ index += 5;
+ var p = new Paragraph { StartTime = ReadTimeCode(buffer, ref index), EndTime = ReadTimeCode(buffer, ref index) };
+ index += 3;
+ for (int i = index; i < index + TextLength; i++)
+ {
+ if (buffer[i] == 0x8f || buffer[i] == 0)
+ {
+ buffer[i] = 32;
+ }
+ else if (buffer[i] == 0x8a)
+ {
+ buffer[i] = 0xa;
+ }
+ }
+ p.Text = Encoding.GetEncoding(1252).GetString(buffer, index, TextLength).Trim();
+ p.Text = p.Text.Replace("\n", Environment.NewLine);
+ return p;
+ }
+
+ private static TimeCode ReadTimeCode(byte[] buffer, ref int index)
+ {
+ int hours = buffer[index];
+ int minutes = buffer[index + 1];
+ int seconds = buffer[index + 2];
+ int milliseconds = FramesToMillisecondsMax999(buffer[index + 3]);
+ index += 4;
+ return new TimeCode(hours, minutes, seconds, milliseconds);
+ }
+
+ public static void WriteSubtitleBlock(FileStream fs, Paragraph p, int number)
+ {
+ fs.WriteByte(0);
+ fs.WriteByte((byte)(number % 256)); // number - low byte
+ fs.WriteByte((byte)(number / 256)); // number - high byte
+ fs.WriteByte(0xff);
+ fs.WriteByte(0);
+ WriteTimeCode(fs, p.StartTime);
+ WriteTimeCode(fs, p.EndTime);
+ fs.WriteByte(1);
+ fs.WriteByte(2);
+ fs.WriteByte(0);
+ var buffer = Encoding.GetEncoding(1252).GetBytes(p.Text.Replace(Environment.NewLine, "Š"));
+ if (buffer.Length <= 128)
+ {
+ fs.Write(buffer, 0, buffer.Length);
+ for (int i = buffer.Length; i < TextLength; i++)
+ {
+ fs.WriteByte(0x8f);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < TextLength; i++)
+ {
+ fs.WriteByte(buffer[i]);
+ }
+ }
+ }
+
+ private static void WriteTimeCode(FileStream fs, TimeCode tc)
+ {
+ fs.WriteByte((byte)(tc.Hours));
+ fs.WriteByte((byte)(tc.Minutes));
+ fs.WriteByte((byte)(tc.Seconds));
+ fs.WriteByte((byte)(MillisecondsToFramesMaxFrameRate(tc.Milliseconds)));
+ }
+
+ public override string Extension => ".stl";
+
+ public const string NameOfFormat = "Avid STL";
+
+ public override string Name => NameOfFormat;
+
+ public static void Save(string fileName, Subtitle subtitle)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ byte[] buffer = { 0x38, 0x35, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30, 0x30, 0x30, 0x39 };
+ fs.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 0xde; i++)
+ {
+ fs.WriteByte(0);
+ }
+
+ string numberOfLines = subtitle.Paragraphs.Count.ToString("D5");
+
+ buffer = Encoding.ASCII.GetBytes(numberOfLines + numberOfLines + "001");
+ fs.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 0x15; i++)
+ {
+ fs.WriteByte(0);
+ }
+
+ buffer = Encoding.ASCII.GetBytes("11");
+ fs.Write(buffer, 0, buffer.Length);
+ while (fs.Length < 1024)
+ {
+ fs.WriteByte(0);
+ }
+
+ int subtitleNumber = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ WriteSubtitleBlock(fs, p, subtitleNumber);
+ subtitleNumber++;
+ }
+ }
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ try
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length > 1150 && fi.Length < 1024000) // not too small or too big
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ if (buffer[0] == 0x38 &&
+ buffer[1] == 0x35 &&
+ buffer[2] == 0x30 &&
+ buffer[1024] == 0 &&
+ buffer[1025] == 0 &&
+ buffer[1026] == 0 &&
+ buffer[1027] == 0xff)
+ {
+ return true;
+ }
+
+ if (fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase) &&
+ buffer.Length > 1283 &&
+ buffer[1024] == 0 &&
+ buffer[1025] == 1 &&
+ buffer[1026] == 0 &&
+ buffer[1027] == 0xff &&
+ buffer[1152] == 0 &&
+ buffer[1153] == 2 &&
+ buffer[1154] == 0 &&
+ buffer[1155] == 0xff &&
+ buffer[1280] == 0 &&
+ buffer[1281] == 3 &&
+ buffer[1282] == 0 &&
+ buffer[1283] == 0xff)
+ {
+ return true;
+ }
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ int index = 1024;
+ while (index <= buffer.Length - 128)
+ {
+ Paragraph p = ReadSubtitleBlock(buffer, index);
+ subtitle.Paragraphs.Add(p);
+ index += 128;
+ }
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/AwsTranscribeJson.cs b/src/libse/SubtitleFormats/AwsTranscribeJson.cs
similarity index 100%
rename from libse/SubtitleFormats/AwsTranscribeJson.cs
rename to src/libse/SubtitleFormats/AwsTranscribeJson.cs
diff --git a/libse/SubtitleFormats/Ayato.cs b/src/libse/SubtitleFormats/Ayato.cs
similarity index 97%
rename from libse/SubtitleFormats/Ayato.cs
rename to src/libse/SubtitleFormats/Ayato.cs
index 7c972c637..4563d19ff 100644
--- a/libse/SubtitleFormats/Ayato.cs
+++ b/src/libse/SubtitleFormats/Ayato.cs
@@ -1,366 +1,366 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Ayato : SubtitleFormat
- {
- public override string Extension => ".aya";
-
- public override string Name => "Ayato";
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 3000 && fi.Length < 1024000) // not too small or too big
- {
- if (!fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- throw new NotImplementedException();
- }
-
- public void Save(string fileName, string videoFileName, Subtitle subtitle)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- // header
- var header = new byte[2713];
- header[00] = 0x05;
- header[01] = 0x30;
- header[02] = 0x32;
- header[03] = 0x2E;
- header[04] = 0x30;
- header[05] = 0x33;
- header[06] = 0x05;
- header[07] = 0x41;
- header[08] = 0x79;
- header[09] = 0x61;
- header[10] = 0x74;
- header[11] = 0x6F;
- header[12] = 0x02;
- header[77] = 0x01;
- header[81] = 0x01;
- header[608] = 0x3b;
- header[609] = 0x32;
- header[613] = 0x58;
- header[614] = 0x02;
- header[615] = 0xE8;
- header[616] = 0x03;
- header[617] = 0xE8;
- header[618] = 0x03;
- header[619] = 0xE8;
- header[620] = 0x03;
- header[621] = 0xE8;
- header[622] = 0x03;
- header[639] = 0x02;
-
- header[682] = (byte)(subtitle.Paragraphs.Count & 0xff);
- header[683] = (byte)((subtitle.Paragraphs.Count >> 8) & 0xff);
-
- header[686] = 0x09;
- header[687] = 0x04;
-
- header[751] = 0x08;
-
- header[760] = 0x01;
- header[761] = 0x0a;
-
- header[830] = 0x58;
- header[831] = 0x48;
-
- header[2048] = 0x99;
- header[2049] = 0x02;
-
- header[2050] = (byte)(subtitle.Paragraphs.Count & 0xff);
- header[2051] = (byte)((subtitle.Paragraphs.Count >> 8) & 0xff);
-
- header[2069] = 0x17;
- header[2071] = 0x17;
- header[2073] = 0x02;
- header[2075] = 0x27;
- header[2077] = 0x0c;
- header[2079] = 0x04;
-
- header[2082] = 0x01;
- header[2085] = 0x01;
-
- // Microsoft Sans Serif
- header[2088] = 0x4d;
- header[2089] = 0x69;
- header[2090] = 0x63;
- header[2091] = 0x72;
- header[2092] = 0x6f;
- header[2093] = 0x73;
- header[2094] = 0x6f;
- header[2095] = 0x66;
- header[2096] = 0x74;
- header[2097] = 0x20;
- header[2098] = 0x53;
- header[2099] = 0x61;
- header[2100] = 0x6e;
- header[2101] = 0x73;
- header[2102] = 0x20;
- header[2103] = 0x53;
- header[2104] = 0x65;
- header[2105] = 0x72;
- header[2106] = 0x69;
- header[2107] = 0x66;
-
- header[2120] = 0x1f;
- header[2123] = 0x01;
- header[2124] = 0x02;
-
- header[2128] = 0xff;
- header[2136] = 0x02;
- header[2176] = 0x01;
- header[2193] = 0x02;
- header[2194] = 0x02;
-
- header[2197] = 0x0C;
- header[2198] = 0x14;
- header[2199] = 0x0C;
- header[2200] = 0x01;
- header[2201] = 0xe8;
- header[2202] = 0x03;
- header[2203] = 0xe8;
- header[2204] = 0x03;
- header[2205] = 0xe8;
- header[2206] = 0x03;
- header[2207] = 0xe8;
- header[2208] = 0x03;
-
- header[2225] = 0x13;
- header[2226] = 0x08;
- header[2227] = 0xdf;
- header[2228] = 0x07;
- header[2229] = 0x13;
- header[2230] = 0x08;
- header[2231] = 0xdf;
- header[2232] = 0x07;
-
- fs.Write(header, 0, header.Length);
-
- // paragraphs
- var sub = new Subtitle(subtitle);
- int number = 1;
- foreach (Paragraph p in sub.Paragraphs)
- {
- WriteParagraph(fs, p, number);
- number++;
- }
- }
- }
-
- private static void WriteParagraph(Stream stream, Paragraph paragraph, int number)
- {
- // subtitle number
- stream.WriteByte((byte)(number & 0xff));
- stream.WriteByte((byte)((number >> 8) & 0xff));
-
- stream.WriteByte(0);
- stream.WriteByte(0);
- WriteFrames(stream, paragraph.StartTime);
- stream.WriteByte(0);
- WriteFrames(stream, paragraph.EndTime);
- stream.WriteByte(0);
-
- stream.WriteByte(0x17);
- stream.WriteByte(0);
- stream.WriteByte(2);
- stream.WriteByte(0);
-
- WriteText(stream, paragraph.Text);
- }
-
- private static void WriteFrames(Stream stream, TimeCode timeCode)
- {
- var frames = (uint)Math.Round((double)MillisecondsToFrames(timeCode.TotalMilliseconds));
- stream.WriteByte((byte)(frames & 0xff));
- stream.WriteByte((byte)((frames >> 8) & 0xff));
- stream.WriteByte((byte)((frames >> 16) & 0xff));
- }
-
- private static void WriteText(Stream stream, string text)
- {
- var bytes = MakeBytes(text);
-
- stream.WriteByte((byte)(bytes.Length + 1)); // text length
-
- for (int i = 0; i < 55; i++) // 55 bytes zero padding
- {
- stream.WriteByte(0);
- }
-
- stream.WriteByte(7);
- stream.Write(bytes, 0, bytes.Length);
- }
-
- private static byte[] MakeBytes(string text)
- {
- var bytesList = new List();
- int count = 0;
- foreach (var line in HtmlUtil.RemoveHtmlTags(text, true).SplitToLines())
- {
- if (count > 0)
- {
- bytesList.Add(0x1f);
- bytesList.Add(0x7);
- }
- bytesList.AddRange(Encoding.UTF8.GetBytes(line));
- count++;
- }
- return bytesList.ToArray();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- const int startPosition = 0xa99;
- const int textPosition = 72;
-
- _errorCount = 0;
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- var buffer = FileUtil.ReadAllBytesShared(fileName);
- int index = startPosition;
- if (buffer[index] != 1)
- {
- return;
- }
-
- while (index + textPosition < buffer.Length)
- {
- int textLength = buffer[index + 16];
- if (textLength > 0 && index + textPosition + textLength < buffer.Length)
- {
- string text = GetText(index + textPosition, textLength, buffer);
- if (!string.IsNullOrWhiteSpace(text))
- {
- int startFrames = GetFrames(index + 4, buffer);
- int endFrames = GetFrames(index + 8, buffer);
- subtitle.Paragraphs.Add(new Paragraph(text, FramesToMilliseconds(startFrames), FramesToMilliseconds(endFrames)));
- }
- }
- index += textPosition + textLength;
- }
- subtitle.Renumber();
- }
-
- private static string GetText(int index, int length, byte[] buffer)
- {
- if (length < 1)
- {
- return string.Empty;
- }
-
- int offset = 0;
- if (buffer[index] == 7)
- {
- offset = 1;
- }
- else if (buffer[index + 1] == 7)
- {
- offset = 2;
- }
- else if (buffer[index + 2] == 7)
- {
- offset = 3;
- }
-
- if (buffer[index + offset] < 32)
- {
- offset++;
- }
-
- if (length - offset < 1)
- {
- return string.Empty;
- }
-
- var sb = new StringBuilder();
- var textBytes = new List();
- int i = index + offset;
- int max = i + length - offset;
- while (i < max)
- {
- if (i + 3 < max && buffer[i] < 32 && buffer[i + 1] < 32 && buffer[i + 2] < 32 && buffer[i + 3] == 7)
- {
- AddToLine(textBytes, sb);
- sb.AppendLine();
- if (i + 4 < max && buffer[i + 4] < 32)
- {
- i++;
- }
- i += 3;
- }
- else if (i + 2 < max && buffer[i] < 32 && buffer[i + 1] < 32 && buffer[i + 2] == 7)
- {
- AddToLine(textBytes, sb);
- sb.AppendLine();
- if (i + 3 < max && buffer[i + 3] < 32)
- {
- i++;
- }
- i += 2;
- }
- else if (i + 1 < max && buffer[i] < 32 && buffer[i + 1] == 7)
- {
- AddToLine(textBytes, sb);
- sb.AppendLine();
- if (i + 2 < max && buffer[i + 2] < 32)
- {
- i++;
- }
- i++;
- }
- else if (buffer[i] == 7)
- {
- AddToLine(textBytes, sb);
- sb.AppendLine();
- if (i + 1 < max && buffer[i + 1] < 32)
- {
- i++;
- }
- }
- else
- {
- textBytes.Add(buffer[i]);
- }
- i++;
- }
- AddToLine(textBytes, sb);
- return sb.ToString();
- }
-
- private static void AddToLine(List textBytes, StringBuilder sb)
- {
- if (textBytes.Count > 0)
- {
- var lineBuffer = textBytes.ToArray();
- sb.Append(Encoding.UTF8.GetString(lineBuffer, 0, lineBuffer.Length));
- textBytes.Clear();
- }
- }
-
- private static int GetFrames(int index, byte[] buffer)
- {
- return (buffer[index + 2] << 16) + (buffer[index + 1] << 8) + buffer[index];
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Ayato : SubtitleFormat
+ {
+ public override string Extension => ".aya";
+
+ public override string Name => "Ayato";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 3000 && fi.Length < 1024000) // not too small or too big
+ {
+ if (!fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Save(string fileName, string videoFileName, Subtitle subtitle)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ // header
+ var header = new byte[2713];
+ header[00] = 0x05;
+ header[01] = 0x30;
+ header[02] = 0x32;
+ header[03] = 0x2E;
+ header[04] = 0x30;
+ header[05] = 0x33;
+ header[06] = 0x05;
+ header[07] = 0x41;
+ header[08] = 0x79;
+ header[09] = 0x61;
+ header[10] = 0x74;
+ header[11] = 0x6F;
+ header[12] = 0x02;
+ header[77] = 0x01;
+ header[81] = 0x01;
+ header[608] = 0x3b;
+ header[609] = 0x32;
+ header[613] = 0x58;
+ header[614] = 0x02;
+ header[615] = 0xE8;
+ header[616] = 0x03;
+ header[617] = 0xE8;
+ header[618] = 0x03;
+ header[619] = 0xE8;
+ header[620] = 0x03;
+ header[621] = 0xE8;
+ header[622] = 0x03;
+ header[639] = 0x02;
+
+ header[682] = (byte)(subtitle.Paragraphs.Count & 0xff);
+ header[683] = (byte)((subtitle.Paragraphs.Count >> 8) & 0xff);
+
+ header[686] = 0x09;
+ header[687] = 0x04;
+
+ header[751] = 0x08;
+
+ header[760] = 0x01;
+ header[761] = 0x0a;
+
+ header[830] = 0x58;
+ header[831] = 0x48;
+
+ header[2048] = 0x99;
+ header[2049] = 0x02;
+
+ header[2050] = (byte)(subtitle.Paragraphs.Count & 0xff);
+ header[2051] = (byte)((subtitle.Paragraphs.Count >> 8) & 0xff);
+
+ header[2069] = 0x17;
+ header[2071] = 0x17;
+ header[2073] = 0x02;
+ header[2075] = 0x27;
+ header[2077] = 0x0c;
+ header[2079] = 0x04;
+
+ header[2082] = 0x01;
+ header[2085] = 0x01;
+
+ // Microsoft Sans Serif
+ header[2088] = 0x4d;
+ header[2089] = 0x69;
+ header[2090] = 0x63;
+ header[2091] = 0x72;
+ header[2092] = 0x6f;
+ header[2093] = 0x73;
+ header[2094] = 0x6f;
+ header[2095] = 0x66;
+ header[2096] = 0x74;
+ header[2097] = 0x20;
+ header[2098] = 0x53;
+ header[2099] = 0x61;
+ header[2100] = 0x6e;
+ header[2101] = 0x73;
+ header[2102] = 0x20;
+ header[2103] = 0x53;
+ header[2104] = 0x65;
+ header[2105] = 0x72;
+ header[2106] = 0x69;
+ header[2107] = 0x66;
+
+ header[2120] = 0x1f;
+ header[2123] = 0x01;
+ header[2124] = 0x02;
+
+ header[2128] = 0xff;
+ header[2136] = 0x02;
+ header[2176] = 0x01;
+ header[2193] = 0x02;
+ header[2194] = 0x02;
+
+ header[2197] = 0x0C;
+ header[2198] = 0x14;
+ header[2199] = 0x0C;
+ header[2200] = 0x01;
+ header[2201] = 0xe8;
+ header[2202] = 0x03;
+ header[2203] = 0xe8;
+ header[2204] = 0x03;
+ header[2205] = 0xe8;
+ header[2206] = 0x03;
+ header[2207] = 0xe8;
+ header[2208] = 0x03;
+
+ header[2225] = 0x13;
+ header[2226] = 0x08;
+ header[2227] = 0xdf;
+ header[2228] = 0x07;
+ header[2229] = 0x13;
+ header[2230] = 0x08;
+ header[2231] = 0xdf;
+ header[2232] = 0x07;
+
+ fs.Write(header, 0, header.Length);
+
+ // paragraphs
+ var sub = new Subtitle(subtitle);
+ int number = 1;
+ foreach (Paragraph p in sub.Paragraphs)
+ {
+ WriteParagraph(fs, p, number);
+ number++;
+ }
+ }
+ }
+
+ private static void WriteParagraph(Stream stream, Paragraph paragraph, int number)
+ {
+ // subtitle number
+ stream.WriteByte((byte)(number & 0xff));
+ stream.WriteByte((byte)((number >> 8) & 0xff));
+
+ stream.WriteByte(0);
+ stream.WriteByte(0);
+ WriteFrames(stream, paragraph.StartTime);
+ stream.WriteByte(0);
+ WriteFrames(stream, paragraph.EndTime);
+ stream.WriteByte(0);
+
+ stream.WriteByte(0x17);
+ stream.WriteByte(0);
+ stream.WriteByte(2);
+ stream.WriteByte(0);
+
+ WriteText(stream, paragraph.Text);
+ }
+
+ private static void WriteFrames(Stream stream, TimeCode timeCode)
+ {
+ var frames = (uint)Math.Round((double)MillisecondsToFrames(timeCode.TotalMilliseconds));
+ stream.WriteByte((byte)(frames & 0xff));
+ stream.WriteByte((byte)((frames >> 8) & 0xff));
+ stream.WriteByte((byte)((frames >> 16) & 0xff));
+ }
+
+ private static void WriteText(Stream stream, string text)
+ {
+ var bytes = MakeBytes(text);
+
+ stream.WriteByte((byte)(bytes.Length + 1)); // text length
+
+ for (int i = 0; i < 55; i++) // 55 bytes zero padding
+ {
+ stream.WriteByte(0);
+ }
+
+ stream.WriteByte(7);
+ stream.Write(bytes, 0, bytes.Length);
+ }
+
+ private static byte[] MakeBytes(string text)
+ {
+ var bytesList = new List();
+ int count = 0;
+ foreach (var line in HtmlUtil.RemoveHtmlTags(text, true).SplitToLines())
+ {
+ if (count > 0)
+ {
+ bytesList.Add(0x1f);
+ bytesList.Add(0x7);
+ }
+ bytesList.AddRange(Encoding.UTF8.GetBytes(line));
+ count++;
+ }
+ return bytesList.ToArray();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ const int startPosition = 0xa99;
+ const int textPosition = 72;
+
+ _errorCount = 0;
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ var buffer = FileUtil.ReadAllBytesShared(fileName);
+ int index = startPosition;
+ if (buffer[index] != 1)
+ {
+ return;
+ }
+
+ while (index + textPosition < buffer.Length)
+ {
+ int textLength = buffer[index + 16];
+ if (textLength > 0 && index + textPosition + textLength < buffer.Length)
+ {
+ string text = GetText(index + textPosition, textLength, buffer);
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ int startFrames = GetFrames(index + 4, buffer);
+ int endFrames = GetFrames(index + 8, buffer);
+ subtitle.Paragraphs.Add(new Paragraph(text, FramesToMilliseconds(startFrames), FramesToMilliseconds(endFrames)));
+ }
+ }
+ index += textPosition + textLength;
+ }
+ subtitle.Renumber();
+ }
+
+ private static string GetText(int index, int length, byte[] buffer)
+ {
+ if (length < 1)
+ {
+ return string.Empty;
+ }
+
+ int offset = 0;
+ if (buffer[index] == 7)
+ {
+ offset = 1;
+ }
+ else if (buffer[index + 1] == 7)
+ {
+ offset = 2;
+ }
+ else if (buffer[index + 2] == 7)
+ {
+ offset = 3;
+ }
+
+ if (buffer[index + offset] < 32)
+ {
+ offset++;
+ }
+
+ if (length - offset < 1)
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder();
+ var textBytes = new List();
+ int i = index + offset;
+ int max = i + length - offset;
+ while (i < max)
+ {
+ if (i + 3 < max && buffer[i] < 32 && buffer[i + 1] < 32 && buffer[i + 2] < 32 && buffer[i + 3] == 7)
+ {
+ AddToLine(textBytes, sb);
+ sb.AppendLine();
+ if (i + 4 < max && buffer[i + 4] < 32)
+ {
+ i++;
+ }
+ i += 3;
+ }
+ else if (i + 2 < max && buffer[i] < 32 && buffer[i + 1] < 32 && buffer[i + 2] == 7)
+ {
+ AddToLine(textBytes, sb);
+ sb.AppendLine();
+ if (i + 3 < max && buffer[i + 3] < 32)
+ {
+ i++;
+ }
+ i += 2;
+ }
+ else if (i + 1 < max && buffer[i] < 32 && buffer[i + 1] == 7)
+ {
+ AddToLine(textBytes, sb);
+ sb.AppendLine();
+ if (i + 2 < max && buffer[i + 2] < 32)
+ {
+ i++;
+ }
+ i++;
+ }
+ else if (buffer[i] == 7)
+ {
+ AddToLine(textBytes, sb);
+ sb.AppendLine();
+ if (i + 1 < max && buffer[i + 1] < 32)
+ {
+ i++;
+ }
+ }
+ else
+ {
+ textBytes.Add(buffer[i]);
+ }
+ i++;
+ }
+ AddToLine(textBytes, sb);
+ return sb.ToString();
+ }
+
+ private static void AddToLine(List textBytes, StringBuilder sb)
+ {
+ if (textBytes.Count > 0)
+ {
+ var lineBuffer = textBytes.ToArray();
+ sb.Append(Encoding.UTF8.GetString(lineBuffer, 0, lineBuffer.Length));
+ textBytes.Clear();
+ }
+ }
+
+ private static int GetFrames(int index, byte[] buffer)
+ {
+ return (buffer[index + 2] << 16) + (buffer[index + 1] << 8) + buffer[index];
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/BdnXml.cs b/src/libse/SubtitleFormats/BdnXml.cs
similarity index 97%
rename from libse/SubtitleFormats/BdnXml.cs
rename to src/libse/SubtitleFormats/BdnXml.cs
index 3bb7fdc17..5b6d1ccc9 100644
--- a/libse/SubtitleFormats/BdnXml.cs
+++ b/src/libse/SubtitleFormats/BdnXml.cs
@@ -1,112 +1,112 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class BdnXml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "BDN Xml";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(xmlStructure);
-
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("Paragraph");
-
- XmlNode number = xml.CreateElement("Number");
- number.InnerText = p.Number.ToString(CultureInfo.InvariantCulture);
- paragraph.AppendChild(number);
-
- XmlNode start = xml.CreateElement("StartMilliseconds");
- start.InnerText = p.StartTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
- paragraph.AppendChild(start);
-
- XmlNode end = xml.CreateElement("EndMilliseconds");
- end.InnerText = p.EndTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
- paragraph.AppendChild(end);
-
- XmlNode text = xml.CreateElement("Text");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
- paragraph.AppendChild(text);
-
- xml.DocumentElement.AppendChild(paragraph);
- }
- string textUtf8;
- using (var ms = new MemoryStream())
- {
- var writer = new XmlTextWriter(ms, Encoding.UTF8) { Formatting = Formatting.Indented };
- xml.Save(writer);
- textUtf8 = Encoding.UTF8.GetString(ms.ToArray());
- }
- return textUtf8.Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string xmlString = sb.ToString();
- if (!xmlString.Contains(" ".xml";
+
+ public override string Name => "BDN Xml";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(xmlStructure);
+
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("Paragraph");
+
+ XmlNode number = xml.CreateElement("Number");
+ number.InnerText = p.Number.ToString(CultureInfo.InvariantCulture);
+ paragraph.AppendChild(number);
+
+ XmlNode start = xml.CreateElement("StartMilliseconds");
+ start.InnerText = p.StartTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
+ paragraph.AppendChild(start);
+
+ XmlNode end = xml.CreateElement("EndMilliseconds");
+ end.InnerText = p.EndTime.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
+ paragraph.AppendChild(end);
+
+ XmlNode text = xml.CreateElement("Text");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
+ paragraph.AppendChild(text);
+
+ xml.DocumentElement.AppendChild(paragraph);
+ }
+ string textUtf8;
+ using (var ms = new MemoryStream())
+ {
+ var writer = new XmlTextWriter(ms, Encoding.UTF8) { Formatting = Formatting.Indented };
+ xml.Save(writer);
+ textUtf8 = Encoding.UTF8.GetString(ms.ToArray());
+ }
+ return textUtf8.Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string xmlString = sb.ToString();
+ if (!xmlString.Contains(" ".stp";
-
- public override string Name => "Belle Nuit Subtitler";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string paragraphWriteFormat = "/tc {0} {1}{2}{3}{2}";
-
- var sb = new StringBuilder();
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, EncodeText(p.Text)));
- }
-
- var doc = new XmlDocument { XmlResolver = null };
- doc.LoadXml("" + Environment.NewLine + @"
- document
-
- creator
- SICT
- type
- STLI
- version
- 1.4
- applicationversion
- Belle Nuit Subtitler 1.7.8
- creationdate
- 2012-03-13 16:30:32
- modificationdate
- 2012-03-13 16:30:32
-
- mainleft
- 40
- maintop
- 48
- mainwidth
- 825
- mainheight
- 886
- styledt
-
- exportdt
-
- previewdt
-
- moviedt
-
- exportformat
- TIFF
- style
-
- font
- Geneva
- size
- 26
- spacing
- 1
- leading
- 7
- bold
-
- italic
-
- underline
-
- vertical
- 486
- halin
- 1
- valign
- 2
- standard
- PAL
- height
- 576
- width
- 720
- widthreal
- 768
- antialiasing
- 4
- left
- 40
- right
- 680
- wrapmethod
- 2
- interlaced
-
- textcolor
- #FBFFF2
- textalpha
- 1
- textsoft
- 0
- bordercolor
- #F0F10
- borderalpha
- 1
- bordersoft
- 0
- borderwidth
- 6
- rectcolor
- #0
- rectalpha
- 0
- rectsoft
- 0
- rectform
- 1
- shadowcolor
- #7F7F7F
- shadowalpha
- 0
- shadowsoft
- 0
- shadowx
- 2
- shadowy
- 2
- framerate
- 25
-
- folderpath
-
- prefix
-
- moviepath
-
- movieoffset
- 00:00:00:00
- moviesyncoption
-
- pagesetup
-
- titlelist
-");
- XmlNode node = doc.CreateElement("string");
- node.InnerText = sb.ToString().Trim() + Environment.NewLine + Environment.NewLine;
- doc.DocumentElement.AppendChild(node);
-
- return ToUtf8XmlString(doc).Replace("\r\n", "\n");
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- foreach (var line in lines)
- {
- sb.AppendLine(line);
- }
- var doc = new XmlDocument { XmlResolver = null };
- try
- {
- doc.LoadXml(sb.ToString());
- if (doc.DocumentElement == null || doc.DocumentElement.Name != "xmldict" || doc.DocumentElement.SelectSingleNode("string") == null)
- {
- return;
- }
- }
- catch (Exception)
- {
- _errorCount = 1;
- return;
- }
-
- string text = null;
- string keyName = string.Empty;
- foreach (XmlNode node in doc.DocumentElement.ChildNodes)
- {
- if (node.Name == "key")
- {
- keyName = node.InnerText;
- }
- else if (node.Name == "string" && keyName == "titlelist")
- {
- text = node.InnerText;
- break;
- }
- }
- if (text == null)
- {
- return;
- }
-
- subtitle.Paragraphs.Clear();
- Paragraph paragraph = null;
- sb.Clear();
- foreach (string line in text.Split(Utilities.NewLineChars))
- {
- if (RegexTimeCode.IsMatch(line))
- {
- string[] parts = line.Substring(4, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 4)
- {
- try
- {
- if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
- {
- paragraph.Text = DecodeText(sb);
- }
-
- var start = DecodeTimeCodeFramesFourParts(parts);
- parts = line.Substring(16, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- var end = DecodeTimeCodeFramesFourParts(parts);
- paragraph = new Paragraph { StartTime = start, EndTime = end };
- subtitle.Paragraphs.Add(paragraph);
- sb.Clear();
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- else if (RegexFileNum.IsMatch(line))
- {
- continue; // skip Belle-Nuit's numbering lines ("/file 0001")
- }
- else if (paragraph != null)
- {
- sb.AppendLine(line);
- }
- else
- {
- _errorCount++;
- }
- }
- if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
- {
- paragraph.Text = DecodeText(sb);
- }
- subtitle.Renumber();
- }
-
- private static string EncodeText(string s)
- {
- s = HtmlUtil.RemoveOpenCloseTags(s, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagFont);
- if (s.StartsWith("{\\an3}", StringComparison.Ordinal) || s.StartsWith("{\\an6}", StringComparison.Ordinal))
- {
- s = "/STYLE RIGHT" + Environment.NewLine + s.Remove(0, 6).Trim();
- }
-
- if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || s.StartsWith("{\\an4}", StringComparison.Ordinal))
- {
- s = "/STYLE LEFT" + Environment.NewLine + s.Remove(0, 6).Trim();
- }
-
- if (s.StartsWith("{\\an7}", StringComparison.Ordinal) || s.StartsWith("{\\an8}", StringComparison.Ordinal) || s.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- s = "/STYLE VERTICAL(-25)" + Environment.NewLine + s.Remove(0, 6).Trim();
- }
-
- if (s.StartsWith("{\\an2}", StringComparison.Ordinal) || s.StartsWith("{\\an5}", StringComparison.Ordinal))
- {
- s = s.Remove(0, 6).Trim();
- }
-
- return s;
- }
-
- private static string DecodeText(StringBuilder sb)
- {
- var s = sb.ToString().Trim();
- s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
- if (s.StartsWith("/STYLE RIGHT" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an3}" + s.Remove(0, 12).Trim();
- }
-
- if (s.StartsWith("/STYLE LEFT" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an1}" + s.Remove(0, 11).Trim();
- }
-
- if (s.StartsWith("/STYLE TOP" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 10).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-25)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-24)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-23)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-22)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-21)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-20)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-19)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an8}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-18)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-17)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-16)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-15)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-14)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-13)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-12)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-11)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- if (s.StartsWith("/STYLE VERTICAL(-10)" + Environment.NewLine, StringComparison.Ordinal))
- {
- s = "{\\an5}" + s.Remove(0, 20).Trim();
- }
-
- s = HtmlUtil.FixInvalidItalicTags(s);
- return s;
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class BelleNuitSubtitler : SubtitleFormat
+ {
+ ///tc 00:00:35:09 00:00:38:05
+ private static readonly Regex RegexTimeCode = new Regex(@"^\/tc \d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d", RegexOptions.Compiled);
+ private static readonly Regex RegexFileNum = new Regex(@"^\/file\s+\d+$", RegexOptions.Compiled);
+
+ public override string Extension => ".stp";
+
+ public override string Name => "Belle Nuit Subtitler";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string paragraphWriteFormat = "/tc {0} {1}{2}{3}{2}";
+
+ var sb = new StringBuilder();
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, EncodeText(p.Text)));
+ }
+
+ var doc = new XmlDocument { XmlResolver = null };
+ doc.LoadXml("" + Environment.NewLine + @"
+ document
+
+ creator
+ SICT
+ type
+ STLI
+ version
+ 1.4
+ applicationversion
+ Belle Nuit Subtitler 1.7.8
+ creationdate
+ 2012-03-13 16:30:32
+ modificationdate
+ 2012-03-13 16:30:32
+
+ mainleft
+ 40
+ maintop
+ 48
+ mainwidth
+ 825
+ mainheight
+ 886
+ styledt
+
+ exportdt
+
+ previewdt
+
+ moviedt
+
+ exportformat
+ TIFF
+ style
+
+ font
+ Geneva
+ size
+ 26
+ spacing
+ 1
+ leading
+ 7
+ bold
+
+ italic
+
+ underline
+
+ vertical
+ 486
+ halin
+ 1
+ valign
+ 2
+ standard
+ PAL
+ height
+ 576
+ width
+ 720
+ widthreal
+ 768
+ antialiasing
+ 4
+ left
+ 40
+ right
+ 680
+ wrapmethod
+ 2
+ interlaced
+
+ textcolor
+ #FBFFF2
+ textalpha
+ 1
+ textsoft
+ 0
+ bordercolor
+ #F0F10
+ borderalpha
+ 1
+ bordersoft
+ 0
+ borderwidth
+ 6
+ rectcolor
+ #0
+ rectalpha
+ 0
+ rectsoft
+ 0
+ rectform
+ 1
+ shadowcolor
+ #7F7F7F
+ shadowalpha
+ 0
+ shadowsoft
+ 0
+ shadowx
+ 2
+ shadowy
+ 2
+ framerate
+ 25
+
+ folderpath
+
+ prefix
+
+ moviepath
+
+ movieoffset
+ 00:00:00:00
+ moviesyncoption
+
+ pagesetup
+
+ titlelist
+");
+ XmlNode node = doc.CreateElement("string");
+ node.InnerText = sb.ToString().Trim() + Environment.NewLine + Environment.NewLine;
+ doc.DocumentElement.AppendChild(node);
+
+ return ToUtf8XmlString(doc).Replace("\r\n", "\n");
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ foreach (var line in lines)
+ {
+ sb.AppendLine(line);
+ }
+ var doc = new XmlDocument { XmlResolver = null };
+ try
+ {
+ doc.LoadXml(sb.ToString());
+ if (doc.DocumentElement == null || doc.DocumentElement.Name != "xmldict" || doc.DocumentElement.SelectSingleNode("string") == null)
+ {
+ return;
+ }
+ }
+ catch (Exception)
+ {
+ _errorCount = 1;
+ return;
+ }
+
+ string text = null;
+ string keyName = string.Empty;
+ foreach (XmlNode node in doc.DocumentElement.ChildNodes)
+ {
+ if (node.Name == "key")
+ {
+ keyName = node.InnerText;
+ }
+ else if (node.Name == "string" && keyName == "titlelist")
+ {
+ text = node.InnerText;
+ break;
+ }
+ }
+ if (text == null)
+ {
+ return;
+ }
+
+ subtitle.Paragraphs.Clear();
+ Paragraph paragraph = null;
+ sb.Clear();
+ foreach (string line in text.Split(Utilities.NewLineChars))
+ {
+ if (RegexTimeCode.IsMatch(line))
+ {
+ string[] parts = line.Substring(4, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 4)
+ {
+ try
+ {
+ if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
+ {
+ paragraph.Text = DecodeText(sb);
+ }
+
+ var start = DecodeTimeCodeFramesFourParts(parts);
+ parts = line.Substring(16, 11).Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ var end = DecodeTimeCodeFramesFourParts(parts);
+ paragraph = new Paragraph { StartTime = start, EndTime = end };
+ subtitle.Paragraphs.Add(paragraph);
+ sb.Clear();
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ else if (RegexFileNum.IsMatch(line))
+ {
+ continue; // skip Belle-Nuit's numbering lines ("/file 0001")
+ }
+ else if (paragraph != null)
+ {
+ sb.AppendLine(line);
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ if (paragraph != null && !string.IsNullOrWhiteSpace(sb.ToString()))
+ {
+ paragraph.Text = DecodeText(sb);
+ }
+ subtitle.Renumber();
+ }
+
+ private static string EncodeText(string s)
+ {
+ s = HtmlUtil.RemoveOpenCloseTags(s, HtmlUtil.TagBold, HtmlUtil.TagUnderline, HtmlUtil.TagFont);
+ if (s.StartsWith("{\\an3}", StringComparison.Ordinal) || s.StartsWith("{\\an6}", StringComparison.Ordinal))
+ {
+ s = "/STYLE RIGHT" + Environment.NewLine + s.Remove(0, 6).Trim();
+ }
+
+ if (s.StartsWith("{\\an1}", StringComparison.Ordinal) || s.StartsWith("{\\an4}", StringComparison.Ordinal))
+ {
+ s = "/STYLE LEFT" + Environment.NewLine + s.Remove(0, 6).Trim();
+ }
+
+ if (s.StartsWith("{\\an7}", StringComparison.Ordinal) || s.StartsWith("{\\an8}", StringComparison.Ordinal) || s.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ s = "/STYLE VERTICAL(-25)" + Environment.NewLine + s.Remove(0, 6).Trim();
+ }
+
+ if (s.StartsWith("{\\an2}", StringComparison.Ordinal) || s.StartsWith("{\\an5}", StringComparison.Ordinal))
+ {
+ s = s.Remove(0, 6).Trim();
+ }
+
+ return s;
+ }
+
+ private static string DecodeText(StringBuilder sb)
+ {
+ var s = sb.ToString().Trim();
+ s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine).Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
+ if (s.StartsWith("/STYLE RIGHT" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an3}" + s.Remove(0, 12).Trim();
+ }
+
+ if (s.StartsWith("/STYLE LEFT" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an1}" + s.Remove(0, 11).Trim();
+ }
+
+ if (s.StartsWith("/STYLE TOP" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 10).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-25)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-24)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-23)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-22)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-21)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-20)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-19)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an8}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-18)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-17)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-16)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-15)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-14)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-13)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-12)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-11)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ if (s.StartsWith("/STYLE VERTICAL(-10)" + Environment.NewLine, StringComparison.Ordinal))
+ {
+ s = "{\\an5}" + s.Remove(0, 20).Trim();
+ }
+
+ s = HtmlUtil.FixInvalidItalicTags(s);
+ return s;
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Bilibili.cs b/src/libse/SubtitleFormats/Bilibili.cs
similarity index 100%
rename from libse/SubtitleFormats/Bilibili.cs
rename to src/libse/SubtitleFormats/Bilibili.cs
diff --git a/libse/SubtitleFormats/CapMakerPlus.cs b/src/libse/SubtitleFormats/CapMakerPlus.cs
similarity index 98%
rename from libse/SubtitleFormats/CapMakerPlus.cs
rename to src/libse/SubtitleFormats/CapMakerPlus.cs
index aaa0123b8..c501c7653 100644
--- a/libse/SubtitleFormats/CapMakerPlus.cs
+++ b/src/libse/SubtitleFormats/CapMakerPlus.cs
@@ -1,277 +1,277 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CapMakerPlus : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
-
- public override string Extension => ".cap";
-
- public const string NameOfFormat = "CapMaker Plus";
-
- public override string Name => NameOfFormat;
-
- public static void Save(string fileName, Subtitle subtitle)
- {
- Paragraph p;
- int gridDataCount = subtitle.Paragraphs.Count;
- for (int i = 0; i < subtitle.Paragraphs.Count; i++)
- {
- p = subtitle.Paragraphs[i];
- Paragraph next = subtitle.GetParagraphOrDefault(i + 1);
- if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100)
- {
- gridDataCount++;
- }
- }
-
- var buffer = new byte[] { 0x2B, 0x27, 0xF, 0x3C, 0x43, 0x61, 0x70, 0x4D, 0x61, 0x6B, 0x65, 0x72, 0x20, 0x50, 0x6C, 0x75, 0x73, 0x3E, 0x3, 0x2, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x21, 0, 0, 0, 0, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x1, 0, 0, 0, 0x5E, 0x1, 0, 0, 0x1E, 0, 0, 0, 0x20, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7D, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0x20, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x1, 0, 0x9, 0, 0x43, 0x47, 0x72, 0x69, 0x64, 0x44, 0x61, 0x74, 0x61, 0x3, 0x2, 0x25, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x4C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x31, 0x1, 0, 0xFF, 0, 0x1, 0, 0, 0, 0, 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x1F, 0x44, 0x3A, 0x5C, 0x43, 0x70, 0x63, 0x57, 0x69, 0x6E, 0x5C, 0x37, 0x30, 0x30, 0x5C, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x73, 0x5C, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x74, 0x78, 0x74, 0x3C, 0, 0, 0, 0x5, 0x41, 0x72, 0x69, 0x61, 0x6C, 0, 0, 0, 0x40, 0xE0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0xB, 0x43, 0x6F, 0x75, 0x72, 0x69, 0x65, 0x72, 0x20, 0x4E, 0x65, 0x77, 0, 0, 0, 0x40, 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0xF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB, 0xFF, 0x4, 0, 0xC, 0, 0, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x1, 0, 0, 0xF0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0 };
- buffer[1400] = (byte)(gridDataCount % 256); // paragraphs - low byte
- buffer[1401] = (byte)(gridDataCount / 256); // paragraphs - high byte
-
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- fs.Write(buffer, 0, buffer.Length);
-
- p = null;
- for (int i = 0; i < subtitle.Paragraphs.Count; i++)
- {
- p = subtitle.Paragraphs[i];
- Paragraph next = subtitle.GetParagraphOrDefault(i + 1);
-
- WriteTime(fs, p.StartTime);
-
- buffer = new byte[] {
- // styles 00 00 80 BF 00 00 00 C0 02 00 01 00
- 0,
- 0,
- 0x80, //horizontal align, 0x80BF= center, 0x0000=left, 0x00c0=right
- 0xBF,
- 0,
- 0,
- 0,
- 0xC0, // vertical Position: C0=bottom, 0=top
- 2, //justification, 1=left, 2=center
- 0,
- 1, //1=normal font, 3=italic
- 0
- };
-
- string text = p.Text;
- if (text.StartsWith("{\\a6}", StringComparison.Ordinal))
- {
- text = p.Text.Remove(0, 5);
- buffer[7] = 0; // align top
- }
- else if (text.StartsWith("{\\a1}", StringComparison.Ordinal))
- {
- text = p.Text.Remove(0, 5);
- buffer[2] = 0; // align left
- buffer[3] = 0; // align left
- }
- else if (text.StartsWith("{\\a3}", StringComparison.Ordinal))
- {
- text = p.Text.Remove(0, 5);
- buffer[2] = 0; // align right
- buffer[3] = 0xc0; // align right
- }
- else if (text.StartsWith("{\\a5}", StringComparison.Ordinal))
- {
- text = p.Text.Remove(0, 5);
- buffer[7] = 0; // align top
- buffer[2] = 0; // align left
- buffer[3] = 0; // align left
- }
- else if (text.StartsWith("{\\a7}", StringComparison.Ordinal))
- {
- text = p.Text.Remove(0, 5);
- buffer[7] = 0; // align top
- buffer[2] = 0; // align right
- buffer[3] = 0xc0; // align right
- }
-
- if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal))
- {
- buffer[10] = 3;
- }
-
- fs.Write(buffer, 0, buffer.Length);
-
- text = HtmlUtil.RemoveHtmlTags(text);
- if (text.Length > 118)
- {
- text = text.Substring(0, 118);
- }
-
- fs.WriteByte((byte)(text.Length));
- buffer = Encoding.GetEncoding(1252).GetBytes(text);
- fs.Write(buffer, 0, buffer.Length);
-
- for (int j = 0; j < 74; j++)
- {
- fs.WriteByte(0);
- }
-
- if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100)
- {
- // write empty end
- WriteTime(fs, p.EndTime);
- buffer = new byte[] { 0, 0, 0, 0xC0, 0, 0, 0, 0, 0x01, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- fs.Write(buffer, 0, buffer.Length);
- }
- }
- if (p != null)
- {
- WriteTime(fs, p.EndTime);
- buffer = new byte[] { 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x02, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x01, 0, 0, 0, 0xFF, 0, 0, 0, 0x01, 0, 0, 0, 0x02, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- fs.Write(buffer, 0, buffer.Length);
- }
- }
- }
-
- private static void WriteTime(FileStream fs, TimeCode timeCode)
- {
- fs.WriteByte(0xb);
- byte[] buffer = Encoding.ASCII.GetBytes(timeCode.ToHHMMSSFF());
- fs.Write(buffer, 0, buffer.Length);
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big
- {
- if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- if (buffer[0] == 0x2b) // "+"
- {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- int i = 128;
- Paragraph last = null;
- while (i < buffer.Length - 20)
- {
- if (buffer[i] == 0x0b)
- {
- string timeCode = Encoding.ASCII.GetString(buffer, i + 1, 11);
- if (timeCode != "00:00:00:00" && RegexTimeCodes.IsMatch(timeCode))
- {
- var p = new Paragraph { StartTime = DecodeTimeCodeFramesFourParts(timeCode.Split(':')) };
- bool italic = buffer[i + 22] == 3; // 3=italic, 1=normal
- int textStart = i + 25; // text starts 25 chars after time code
- int textLength = 0;
- while (textStart + textLength < buffer.Length && buffer[textStart + textLength] != 0)
- {
- textLength++;
- }
- if (textLength > 0)
- {
- p.Text = Encoding.GetEncoding(1252).GetString(buffer, textStart, textLength);
- int rtIndex = p.Text.IndexOf("{\\rtf1", StringComparison.Ordinal);
- if (rtIndex >= 0 && rtIndex < 10)
- {
- p.Text = p.Text.Substring(rtIndex).FromRtf();
- }
- else if (italic)
- {
- p.Text = "" + p.Text + "";
- }
- }
- else
- {
- p.Text = string.Empty;
- }
- last = p;
- subtitle.Paragraphs.Add(p);
- }
- }
- i++;
- }
- if (last != null)
- {
- last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
- }
-
- for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
- {
- subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds;
- }
- for (i = subtitle.Paragraphs.Count - 1; i >= 0; i--)
- {
- if (string.IsNullOrEmpty(subtitle.Paragraphs[i].Text))
- {
- subtitle.Paragraphs.RemoveAt(i);
- }
- }
-
- var deletes = new List();
- for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
- {
- if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
- {
- subtitle.Paragraphs[i].Text += Environment.NewLine + subtitle.Paragraphs[i + 1].Text;
- subtitle.Paragraphs[i].EndTime = subtitle.Paragraphs[i + 1].EndTime;
- deletes.Add(i + 1);
- }
- }
- subtitle.RemoveParagraphsByIndices(deletes);
-
- for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
- {
- if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
- {
- }
- else if (subtitle.Paragraphs[i].EndTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
- {
- subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds - 1;
- }
- }
- subtitle.Renumber();
-
- // adjust all times
- if (buffer.Length > 1364)
- {
- try
- {
- string adjust = Encoding.GetEncoding(1252).GetString(buffer, 1354, 11); // 00:59:59:28
- TimeCode tc = DecodeTimeCodeFramesFourParts(adjust.Split(':'));
- if (tc.TotalMilliseconds > 0)
- {
- subtitle.AddTimeToAllParagraphs(TimeSpan.FromMilliseconds(-tc.TotalMilliseconds));
- }
- }
- catch
- {
- // ignored
- }
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CapMakerPlus : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
+
+ public override string Extension => ".cap";
+
+ public const string NameOfFormat = "CapMaker Plus";
+
+ public override string Name => NameOfFormat;
+
+ public static void Save(string fileName, Subtitle subtitle)
+ {
+ Paragraph p;
+ int gridDataCount = subtitle.Paragraphs.Count;
+ for (int i = 0; i < subtitle.Paragraphs.Count; i++)
+ {
+ p = subtitle.Paragraphs[i];
+ Paragraph next = subtitle.GetParagraphOrDefault(i + 1);
+ if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100)
+ {
+ gridDataCount++;
+ }
+ }
+
+ var buffer = new byte[] { 0x2B, 0x27, 0xF, 0x3C, 0x43, 0x61, 0x70, 0x4D, 0x61, 0x6B, 0x65, 0x72, 0x20, 0x50, 0x6C, 0x75, 0x73, 0x3E, 0x3, 0x2, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x21, 0, 0, 0, 0, 0, 0, 0, 0x7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x5, 0, 0, 0, 0x1, 0, 0, 0, 0x5E, 0x1, 0, 0, 0x1E, 0, 0, 0, 0x20, 0, 0, 0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7D, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x42, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0x20, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x1, 0, 0, 0, 0xFF, 0, 0, 0, 0x1, 0, 0, 0, 0x2, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x1, 0, 0x9, 0, 0x43, 0x47, 0x72, 0x69, 0x64, 0x44, 0x61, 0x74, 0x61, 0x3, 0x2, 0x25, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x4C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x31, 0x1, 0, 0xFF, 0, 0x1, 0, 0, 0, 0, 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0x1F, 0x44, 0x3A, 0x5C, 0x43, 0x70, 0x63, 0x57, 0x69, 0x6E, 0x5C, 0x37, 0x30, 0x30, 0x5C, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x73, 0x5C, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x74, 0x78, 0x74, 0x3C, 0, 0, 0, 0x5, 0x41, 0x72, 0x69, 0x61, 0x6C, 0, 0, 0, 0x40, 0xE0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0xB, 0x43, 0x6F, 0x75, 0x72, 0x69, 0x65, 0x72, 0x20, 0x4E, 0x65, 0x77, 0, 0, 0, 0x40, 0xB4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0xF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB, 0xFF, 0x4, 0, 0xC, 0, 0, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0x1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x1, 0, 0, 0xF0, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0x14, 0, 0, 0, 0xA, 0, 0, 0, 0, 0, 0, 0x9, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x2E, 0x63, 0x61, 0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xB, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0, 0, 0, 0 };
+ buffer[1400] = (byte)(gridDataCount % 256); // paragraphs - low byte
+ buffer[1401] = (byte)(gridDataCount / 256); // paragraphs - high byte
+
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ fs.Write(buffer, 0, buffer.Length);
+
+ p = null;
+ for (int i = 0; i < subtitle.Paragraphs.Count; i++)
+ {
+ p = subtitle.Paragraphs[i];
+ Paragraph next = subtitle.GetParagraphOrDefault(i + 1);
+
+ WriteTime(fs, p.StartTime);
+
+ buffer = new byte[] {
+ // styles 00 00 80 BF 00 00 00 C0 02 00 01 00
+ 0,
+ 0,
+ 0x80, //horizontal align, 0x80BF= center, 0x0000=left, 0x00c0=right
+ 0xBF,
+ 0,
+ 0,
+ 0,
+ 0xC0, // vertical Position: C0=bottom, 0=top
+ 2, //justification, 1=left, 2=center
+ 0,
+ 1, //1=normal font, 3=italic
+ 0
+ };
+
+ string text = p.Text;
+ if (text.StartsWith("{\\a6}", StringComparison.Ordinal))
+ {
+ text = p.Text.Remove(0, 5);
+ buffer[7] = 0; // align top
+ }
+ else if (text.StartsWith("{\\a1}", StringComparison.Ordinal))
+ {
+ text = p.Text.Remove(0, 5);
+ buffer[2] = 0; // align left
+ buffer[3] = 0; // align left
+ }
+ else if (text.StartsWith("{\\a3}", StringComparison.Ordinal))
+ {
+ text = p.Text.Remove(0, 5);
+ buffer[2] = 0; // align right
+ buffer[3] = 0xc0; // align right
+ }
+ else if (text.StartsWith("{\\a5}", StringComparison.Ordinal))
+ {
+ text = p.Text.Remove(0, 5);
+ buffer[7] = 0; // align top
+ buffer[2] = 0; // align left
+ buffer[3] = 0; // align left
+ }
+ else if (text.StartsWith("{\\a7}", StringComparison.Ordinal))
+ {
+ text = p.Text.Remove(0, 5);
+ buffer[7] = 0; // align top
+ buffer[2] = 0; // align right
+ buffer[3] = 0xc0; // align right
+ }
+
+ if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal))
+ {
+ buffer[10] = 3;
+ }
+
+ fs.Write(buffer, 0, buffer.Length);
+
+ text = HtmlUtil.RemoveHtmlTags(text);
+ if (text.Length > 118)
+ {
+ text = text.Substring(0, 118);
+ }
+
+ fs.WriteByte((byte)(text.Length));
+ buffer = Encoding.GetEncoding(1252).GetBytes(text);
+ fs.Write(buffer, 0, buffer.Length);
+
+ for (int j = 0; j < 74; j++)
+ {
+ fs.WriteByte(0);
+ }
+
+ if (next != null && next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100)
+ {
+ // write empty end
+ WriteTime(fs, p.EndTime);
+ buffer = new byte[] { 0, 0, 0, 0xC0, 0, 0, 0, 0, 0x01, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ fs.Write(buffer, 0, buffer.Length);
+ }
+ }
+ if (p != null)
+ {
+ WriteTime(fs, p.EndTime);
+ buffer = new byte[] { 0, 0, 0x80, 0xBF, 0, 0, 0, 0xC0, 0x02, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0x40, 0x40, 0, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0x80, 0x80, 0x80, 0, 0x01, 0, 0, 0, 0xFF, 0, 0, 0, 0x01, 0, 0, 0, 0x02, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x48, 0, 0, 0, 0x30, 0, 0, 0, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ fs.Write(buffer, 0, buffer.Length);
+ }
+ }
+ }
+
+ private static void WriteTime(FileStream fs, TimeCode timeCode)
+ {
+ fs.WriteByte(0xb);
+ byte[] buffer = Encoding.ASCII.GetBytes(timeCode.ToHHMMSSFF());
+ fs.Write(buffer, 0, buffer.Length);
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big
+ {
+ if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ if (buffer[0] == 0x2b) // "+"
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ int i = 128;
+ Paragraph last = null;
+ while (i < buffer.Length - 20)
+ {
+ if (buffer[i] == 0x0b)
+ {
+ string timeCode = Encoding.ASCII.GetString(buffer, i + 1, 11);
+ if (timeCode != "00:00:00:00" && RegexTimeCodes.IsMatch(timeCode))
+ {
+ var p = new Paragraph { StartTime = DecodeTimeCodeFramesFourParts(timeCode.Split(':')) };
+ bool italic = buffer[i + 22] == 3; // 3=italic, 1=normal
+ int textStart = i + 25; // text starts 25 chars after time code
+ int textLength = 0;
+ while (textStart + textLength < buffer.Length && buffer[textStart + textLength] != 0)
+ {
+ textLength++;
+ }
+ if (textLength > 0)
+ {
+ p.Text = Encoding.GetEncoding(1252).GetString(buffer, textStart, textLength);
+ int rtIndex = p.Text.IndexOf("{\\rtf1", StringComparison.Ordinal);
+ if (rtIndex >= 0 && rtIndex < 10)
+ {
+ p.Text = p.Text.Substring(rtIndex).FromRtf();
+ }
+ else if (italic)
+ {
+ p.Text = "" + p.Text + "";
+ }
+ }
+ else
+ {
+ p.Text = string.Empty;
+ }
+ last = p;
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ i++;
+ }
+ if (last != null)
+ {
+ last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
+ }
+
+ for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
+ {
+ subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds;
+ }
+ for (i = subtitle.Paragraphs.Count - 1; i >= 0; i--)
+ {
+ if (string.IsNullOrEmpty(subtitle.Paragraphs[i].Text))
+ {
+ subtitle.Paragraphs.RemoveAt(i);
+ }
+ }
+
+ var deletes = new List();
+ for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
+ {
+ if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
+ {
+ subtitle.Paragraphs[i].Text += Environment.NewLine + subtitle.Paragraphs[i + 1].Text;
+ subtitle.Paragraphs[i].EndTime = subtitle.Paragraphs[i + 1].EndTime;
+ deletes.Add(i + 1);
+ }
+ }
+ subtitle.RemoveParagraphsByIndices(deletes);
+
+ for (i = 0; i < subtitle.Paragraphs.Count - 1; i++)
+ {
+ if (subtitle.Paragraphs[i].StartTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
+ {
+ }
+ else if (subtitle.Paragraphs[i].EndTime.TotalMilliseconds == subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds)
+ {
+ subtitle.Paragraphs[i].EndTime.TotalMilliseconds = subtitle.Paragraphs[i + 1].StartTime.TotalMilliseconds - 1;
+ }
+ }
+ subtitle.Renumber();
+
+ // adjust all times
+ if (buffer.Length > 1364)
+ {
+ try
+ {
+ string adjust = Encoding.GetEncoding(1252).GetString(buffer, 1354, 11); // 00:59:59:28
+ TimeCode tc = DecodeTimeCodeFramesFourParts(adjust.Split(':'));
+ if (tc.TotalMilliseconds > 0)
+ {
+ subtitle.AddTimeToAllParagraphs(TimeSpan.FromMilliseconds(-tc.TotalMilliseconds));
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Cappella.cs b/src/libse/SubtitleFormats/Cappella.cs
similarity index 100%
rename from libse/SubtitleFormats/Cappella.cs
rename to src/libse/SubtitleFormats/Cappella.cs
diff --git a/libse/SubtitleFormats/CaptionAssistant.cs b/src/libse/SubtitleFormats/CaptionAssistant.cs
similarity index 97%
rename from libse/SubtitleFormats/CaptionAssistant.cs
rename to src/libse/SubtitleFormats/CaptionAssistant.cs
index ad33004a1..47dc9c78a 100644
--- a/libse/SubtitleFormats/CaptionAssistant.cs
+++ b/src/libse/SubtitleFormats/CaptionAssistant.cs
@@ -1,156 +1,156 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CaptionAssistant : SubtitleFormat
- {
- public override string Extension => ".cac";
-
- public override string Name => "Caption Assistant";
-
- private static string ToTimeCode(TimeCode time)
- {
- return time.ToHHMMSSFF();
- }
-
- private static TimeCode DecodeTimeCode(string s)
- {
- var parts = s.Split(new[] { ':', ';' }, StringSplitOptions.RemoveEmptyEntries);
- var hour = int.Parse(parts[0]);
- var minutes = int.Parse(parts[1]);
- var seconds = int.Parse(parts[2]);
- var frames = int.Parse(parts[3]);
-
- int milliseconds = (int)Math.Round(((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * frames));
- if (milliseconds > 999)
- {
- milliseconds = 999;
- }
-
- return new TimeCode(hour, minutes, seconds, milliseconds);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.XmlResolver = null;
- var cd = xml.DocumentElement.SelectSingleNode("CaptionData");
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("CaptionDetail");
-
- XmlAttribute start = xml.CreateAttribute("PositionIn");
- start.InnerText = ToTimeCode(p.StartTime);
- paragraph.Attributes.Append(start);
-
- XmlAttribute end = xml.CreateAttribute("PositionOut");
- end.InnerText = ToTimeCode(p.EndTime);
- paragraph.Attributes.Append(end);
-
- XmlAttribute text = xml.CreateAttribute("CaptionText");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
- paragraph.Attributes.Append(text);
-
- XmlAttribute align = xml.CreateAttribute("Align");
- if (p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal))
- {
- align.InnerText = "Left";
- }
- else if (p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- align.InnerText = "Right";
- }
- else
- {
- align.InnerText = "Center";
- }
-
- paragraph.Attributes.Append(align);
-
- XmlAttribute captionType = xml.CreateAttribute("CaptionType");
- captionType.InnerText = "608CC1";
- paragraph.Attributes.Append(captionType);
-
- cd.AppendChild(paragraph);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string allText = sb.ToString();
- if (!allText.Contains("") || !allText.Contains(""))
- {
- return;
- }
-
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(allText);
- }
- catch (Exception exception)
- {
- System.Diagnostics.Debug.WriteLine(exception.Message);
- _errorCount = 1;
- return;
- }
-
- if (xml.DocumentElement == null)
- {
- _errorCount = 1;
- return;
- }
-
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("CaptionData/CaptionDetail"))
- {
- try
- {
- if (node.Attributes != null)
- {
- string text = node.Attributes.GetNamedItem("CaptionText").InnerText.Trim();
-
- if (node.Attributes.GetNamedItem("Align") != null)
- {
- string align = node.Attributes.GetNamedItem("Align").InnerText.Trim();
- if (align.Equals("left", StringComparison.OrdinalIgnoreCase))
- {
- text = "{\\an1}" + text;
- }
- else if (align.Equals("right", StringComparison.OrdinalIgnoreCase))
- {
- text = "{\\an3}" + text;
- }
- }
-
- string start = node.Attributes.GetNamedItem("PositionIn").InnerText;
- string end = node.Attributes.GetNamedItem("PositionOut").InnerText;
- subtitle.Paragraphs.Add(new Paragraph(DecodeTimeCode(start), DecodeTimeCode(end), text));
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CaptionAssistant : SubtitleFormat
+ {
+ public override string Extension => ".cac";
+
+ public override string Name => "Caption Assistant";
+
+ private static string ToTimeCode(TimeCode time)
+ {
+ return time.ToHHMMSSFF();
+ }
+
+ private static TimeCode DecodeTimeCode(string s)
+ {
+ var parts = s.Split(new[] { ':', ';' }, StringSplitOptions.RemoveEmptyEntries);
+ var hour = int.Parse(parts[0]);
+ var minutes = int.Parse(parts[1]);
+ var seconds = int.Parse(parts[2]);
+ var frames = int.Parse(parts[3]);
+
+ int milliseconds = (int)Math.Round(((TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * frames));
+ if (milliseconds > 999)
+ {
+ milliseconds = 999;
+ }
+
+ return new TimeCode(hour, minutes, seconds, milliseconds);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.XmlResolver = null;
+ var cd = xml.DocumentElement.SelectSingleNode("CaptionData");
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("CaptionDetail");
+
+ XmlAttribute start = xml.CreateAttribute("PositionIn");
+ start.InnerText = ToTimeCode(p.StartTime);
+ paragraph.Attributes.Append(start);
+
+ XmlAttribute end = xml.CreateAttribute("PositionOut");
+ end.InnerText = ToTimeCode(p.EndTime);
+ paragraph.Attributes.Append(end);
+
+ XmlAttribute text = xml.CreateAttribute("CaptionText");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
+ paragraph.Attributes.Append(text);
+
+ XmlAttribute align = xml.CreateAttribute("Align");
+ if (p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal))
+ {
+ align.InnerText = "Left";
+ }
+ else if (p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ align.InnerText = "Right";
+ }
+ else
+ {
+ align.InnerText = "Center";
+ }
+
+ paragraph.Attributes.Append(align);
+
+ XmlAttribute captionType = xml.CreateAttribute("CaptionType");
+ captionType.InnerText = "608CC1";
+ paragraph.Attributes.Append(captionType);
+
+ cd.AppendChild(paragraph);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string allText = sb.ToString();
+ if (!allText.Contains("") || !allText.Contains(""))
+ {
+ return;
+ }
+
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(allText);
+ }
+ catch (Exception exception)
+ {
+ System.Diagnostics.Debug.WriteLine(exception.Message);
+ _errorCount = 1;
+ return;
+ }
+
+ if (xml.DocumentElement == null)
+ {
+ _errorCount = 1;
+ return;
+ }
+
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("CaptionData/CaptionDetail"))
+ {
+ try
+ {
+ if (node.Attributes != null)
+ {
+ string text = node.Attributes.GetNamedItem("CaptionText").InnerText.Trim();
+
+ if (node.Attributes.GetNamedItem("Align") != null)
+ {
+ string align = node.Attributes.GetNamedItem("Align").InnerText.Trim();
+ if (align.Equals("left", StringComparison.OrdinalIgnoreCase))
+ {
+ text = "{\\an1}" + text;
+ }
+ else if (align.Equals("right", StringComparison.OrdinalIgnoreCase))
+ {
+ text = "{\\an3}" + text;
+ }
+ }
+
+ string start = node.Attributes.GetNamedItem("PositionIn").InnerText;
+ string end = node.Attributes.GetNamedItem("PositionOut").InnerText;
+ subtitle.Paragraphs.Add(new Paragraph(DecodeTimeCode(start), DecodeTimeCode(end), text));
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Captionate.cs b/src/libse/SubtitleFormats/Captionate.cs
similarity index 97%
rename from libse/SubtitleFormats/Captionate.cs
rename to src/libse/SubtitleFormats/Captionate.cs
index 319fece29..970e69ba3 100644
--- a/libse/SubtitleFormats/Captionate.cs
+++ b/src/libse/SubtitleFormats/Captionate.cs
@@ -1,165 +1,165 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Captionate : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public const string NameOfFormat = "Captionate";
-
- public override string Name => NameOfFormat;
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string xmlStructure = @"
-hh:mm:ss:ff/30
-namesareprefixed
-
-
-
-
-
-
-
-";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- Paragraph last = null;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- if (last != null)
- {
- if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds)
- {
- var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } };
- AddParagraph(xml, blank);
- }
- }
-
- AddParagraph(xml, p);
- last = p;
- }
-
- return ToUtf8XmlString(xml, true);
- }
-
- private static void AddParagraph(XmlDocument xml, Paragraph p)
- {
- XmlNode paragraph = xml.CreateElement("caption");
-
- XmlAttribute start = xml.CreateAttribute("time");
- start.InnerText = EncodeTime(p.StartTime);
- paragraph.Attributes.Append(start);
-
- if (!string.IsNullOrWhiteSpace(p.Text))
- {
- XmlNode tracks = xml.CreateElement("tracks");
- paragraph.AppendChild(tracks);
-
- XmlNode track0 = xml.CreateElement("track0");
- track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
- track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
");
- tracks.AppendChild(track0);
- }
- xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- string xmlString;
-
- if (lines == null && fileName != null)
- {
- xmlString = File.ReadAllText(fileName);
- }
- else if (lines != null)
- {
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- xmlString = sb.ToString();
- }
- else
- {
- return;
- }
-
- if (!xmlString.Contains("") || !xmlString.Contains(""))
- {
- return;
- }
-
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(xmlString);
- }
- catch
- {
- _errorCount = 1;
- return;
- }
-
- Paragraph p = null;
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption"))
- {
- try
- {
- if (node.Attributes["time"] != null)
- {
- string start = node.Attributes["time"].InnerText;
- double startMilliseconds = DecodeTimeToMilliseconds(start);
- if (p != null)
- {
- p.EndTime.TotalMilliseconds = startMilliseconds - 1;
- }
-
- if (node.SelectSingleNode("tracks/track0") != null)
- {
- string text = node.SelectSingleNode("tracks/track0").InnerText;
- text = HtmlUtil.RemoveHtmlTags(text);
- text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine);
- p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000);
- if (!string.IsNullOrWhiteSpace(text))
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
-
- private static double DecodeTimeToMilliseconds(string time)
- {
- string[] parts = time.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- return new TimeSpan(0, int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), (int)(int.Parse(parts[3]) * 10.0)).TotalMilliseconds;
- }
-
- private static string EncodeTime(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{time.Milliseconds / 10.0:00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Captionate : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public const string NameOfFormat = "Captionate";
+
+ public override string Name => NameOfFormat;
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string xmlStructure = @"
+hh:mm:ss:ff/30
+namesareprefixed
+
+
+
+
+
+
+
+";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ Paragraph last = null;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (last != null)
+ {
+ if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds)
+ {
+ var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } };
+ AddParagraph(xml, blank);
+ }
+ }
+
+ AddParagraph(xml, p);
+ last = p;
+ }
+
+ return ToUtf8XmlString(xml, true);
+ }
+
+ private static void AddParagraph(XmlDocument xml, Paragraph p)
+ {
+ XmlNode paragraph = xml.CreateElement("caption");
+
+ XmlAttribute start = xml.CreateAttribute("time");
+ start.InnerText = EncodeTime(p.StartTime);
+ paragraph.Attributes.Append(start);
+
+ if (!string.IsNullOrWhiteSpace(p.Text))
+ {
+ XmlNode tracks = xml.CreateElement("tracks");
+ paragraph.AppendChild(tracks);
+
+ XmlNode track0 = xml.CreateElement("track0");
+ track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
+ track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
");
+ tracks.AppendChild(track0);
+ }
+ xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ string xmlString;
+
+ if (lines == null && fileName != null)
+ {
+ xmlString = File.ReadAllText(fileName);
+ }
+ else if (lines != null)
+ {
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ xmlString = sb.ToString();
+ }
+ else
+ {
+ return;
+ }
+
+ if (!xmlString.Contains("") || !xmlString.Contains(""))
+ {
+ return;
+ }
+
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(xmlString);
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+
+ Paragraph p = null;
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption"))
+ {
+ try
+ {
+ if (node.Attributes["time"] != null)
+ {
+ string start = node.Attributes["time"].InnerText;
+ double startMilliseconds = DecodeTimeToMilliseconds(start);
+ if (p != null)
+ {
+ p.EndTime.TotalMilliseconds = startMilliseconds - 1;
+ }
+
+ if (node.SelectSingleNode("tracks/track0") != null)
+ {
+ string text = node.SelectSingleNode("tracks/track0").InnerText;
+ text = HtmlUtil.RemoveHtmlTags(text);
+ text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine);
+ p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000);
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static double DecodeTimeToMilliseconds(string time)
+ {
+ string[] parts = time.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ return new TimeSpan(0, int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), (int)(int.Parse(parts[3]) * 10.0)).TotalMilliseconds;
+ }
+
+ private static string EncodeTime(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{time.Milliseconds / 10.0:00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/CaptionateMs.cs b/src/libse/SubtitleFormats/CaptionateMs.cs
similarity index 97%
rename from libse/SubtitleFormats/CaptionateMs.cs
rename to src/libse/SubtitleFormats/CaptionateMs.cs
index a57982704..300978750 100644
--- a/libse/SubtitleFormats/CaptionateMs.cs
+++ b/src/libse/SubtitleFormats/CaptionateMs.cs
@@ -1,145 +1,145 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CaptionateMs : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Captionate MS";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string xmlStructure = @"
-ms
-namesareprefixed
-
-
-
-
-
-
-
-";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- Paragraph last = null;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- if (last != null)
- {
- if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds)
- {
- var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } };
- AddParagraph(xml, blank);
- }
- }
-
- AddParagraph(xml, p);
- last = p;
- }
-
- return ToUtf8XmlString(xml, true);
- }
-
- private static void AddParagraph(XmlDocument xml, Paragraph p)
- {
- XmlNode paragraph = xml.CreateElement("caption");
-
- XmlAttribute start = xml.CreateAttribute("time");
- start.InnerText = EncodeTime(p.StartTime);
- paragraph.Attributes.Append(start);
-
- if (!string.IsNullOrWhiteSpace(p.Text))
- {
- XmlNode tracks = xml.CreateElement("tracks");
- paragraph.AppendChild(tracks);
-
- XmlNode track0 = xml.CreateElement("track0");
- track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
- track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
");
- tracks.AppendChild(track0);
- }
- xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string xmlString = sb.ToString();
- if (!xmlString.Contains("") || !xmlString.Contains(""))
- {
- return;
- }
-
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(xmlString);
- }
- catch
- {
- _errorCount = 1;
- return;
- }
-
- Paragraph p = null;
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption"))
- {
- try
- {
- if (node.Attributes["time"] != null)
- {
- string start = node.Attributes["time"].InnerText;
- double startMilliseconds = double.Parse(start);
- if (p != null)
- {
- p.EndTime.TotalMilliseconds = startMilliseconds - 1;
- }
-
- if (node.SelectSingleNode("tracks/track0") != null)
- {
- string text = node.SelectSingleNode("tracks/track0").InnerText;
- text = HtmlUtil.RemoveHtmlTags(text);
- text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine);
- p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000);
- if (!string.IsNullOrWhiteSpace(text))
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
-
- private static string EncodeTime(TimeCode time)
- {
- return time.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CaptionateMs : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Captionate MS";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string xmlStructure = @"
+ms
+namesareprefixed
+
+
+
+
+
+
+
+";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ Paragraph last = null;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (last != null)
+ {
+ if (last.EndTime.TotalMilliseconds + 500 < p.StartTime.TotalMilliseconds)
+ {
+ var blank = new Paragraph { StartTime = { TotalMilliseconds = last.EndTime.TotalMilliseconds } };
+ AddParagraph(xml, blank);
+ }
+ }
+
+ AddParagraph(xml, p);
+ last = p;
+ }
+
+ return ToUtf8XmlString(xml, true);
+ }
+
+ private static void AddParagraph(XmlDocument xml, Paragraph p)
+ {
+ XmlNode paragraph = xml.CreateElement("caption");
+
+ XmlAttribute start = xml.CreateAttribute("time");
+ start.InnerText = EncodeTime(p.StartTime);
+ paragraph.Attributes.Append(start);
+
+ if (!string.IsNullOrWhiteSpace(p.Text))
+ {
+ XmlNode tracks = xml.CreateElement("tracks");
+ paragraph.AppendChild(tracks);
+
+ XmlNode track0 = xml.CreateElement("track0");
+ track0.InnerText = HtmlUtil.RemoveHtmlTags(p.Text, true);
+ track0.InnerXml = track0.InnerXml.Replace(Environment.NewLine, "
");
+ tracks.AppendChild(track0);
+ }
+ xml.DocumentElement.SelectSingleNode("captions").AppendChild(paragraph);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string xmlString = sb.ToString();
+ if (!xmlString.Contains("") || !xmlString.Contains(""))
+ {
+ return;
+ }
+
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(xmlString);
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+
+ Paragraph p = null;
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("captions/caption"))
+ {
+ try
+ {
+ if (node.Attributes["time"] != null)
+ {
+ string start = node.Attributes["time"].InnerText;
+ double startMilliseconds = double.Parse(start);
+ if (p != null)
+ {
+ p.EndTime.TotalMilliseconds = startMilliseconds - 1;
+ }
+
+ if (node.SelectSingleNode("tracks/track0") != null)
+ {
+ string text = node.SelectSingleNode("tracks/track0").InnerText;
+ text = HtmlUtil.RemoveHtmlTags(text);
+ text = text.Replace("
", Environment.NewLine).Replace("
", Environment.NewLine).Replace("
", Environment.NewLine);
+ p = new Paragraph(text, startMilliseconds, startMilliseconds + 3000);
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static string EncodeTime(TimeCode time)
+ {
+ return time.TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/CaptionsInc.cs b/src/libse/SubtitleFormats/CaptionsInc.cs
similarity index 97%
rename from libse/SubtitleFormats/CaptionsInc.cs
rename to src/libse/SubtitleFormats/CaptionsInc.cs
index 2a5ec45d7..c852662ff 100644
--- a/libse/SubtitleFormats/CaptionsInc.cs
+++ b/src/libse/SubtitleFormats/CaptionsInc.cs
@@ -1,264 +1,264 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CaptionsInc : SubtitleFormat
- {
- public override string Extension => ".cin";
-
- public override string Name => "Caption Inc";
-
- public static void Save(string fileName, Subtitle subtitle)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- string name = Path.GetFileNameWithoutExtension(fileName);
- byte[] buffer = Encoding.ASCII.GetBytes(name);
- for (int i = 0; i < buffer.Length && i < 8; i++)
- {
- fs.WriteByte(buffer[i]);
- }
-
- while (fs.Length < 8)
- {
- fs.WriteByte(0x20);
- }
-
- WriteTime(fs, subtitle.Paragraphs[0].StartTime, false); // first start time
- WriteTime(fs, subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime, false); // last end time
-
- buffer = Encoding.ASCII.GetBytes("Generic Unknown Unknown \"\" Unknown Unknown Unknown".PadRight(230, ' '));
- fs.Write(buffer, 0, buffer.Length);
-
- // paragraphs
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- buffer = new byte[] { 0x0D, 0x0A, 0xFE }; // header
- fs.Write(buffer, 0, buffer.Length);
-
- // styles
- var text = new List { 0x14, 0x20, 0x14, 0x2E, 0x14, 0x54, 0x17 };
- int noOfLines = Utilities.GetNumberOfLines(p.Text);
- text.Add(noOfLines == 1 ? (byte)0x22 : (byte)0x21);
-
- var lines = p.Text.Split(Utilities.NewLineChars, StringSplitOptions.None);
- foreach (string line in lines)
- {
- foreach (char ch in line)
- {
- text.Add(Encoding.GetEncoding(1252).GetBytes(new[] { ch })[0]);
- }
- text.Add(0x14);
- text.Add(0x74);
- }
-
- // codes+text length
- buffer = Encoding.ASCII.GetBytes($"{text.Count:000}");
- fs.Write(buffer, 0, buffer.Length);
-
- WriteTime(fs, p.StartTime, true);
-
- // write codes + text
- foreach (byte b in text)
- {
- fs.WriteByte(b);
- }
-
- buffer = new byte[] { 0x14, 0x2F, 0x0D, 0x0A, 0xFE, 0x30, 0x30, 0x32, 0x30 };
- fs.Write(buffer, 0, buffer.Length);
- WriteTime(fs, p.EndTime, true);
- }
- }
- }
-
- private static void WriteTime(FileStream fs, TimeCode timeCode, bool addEndBytes)
- {
- var time = timeCode.ToHHMMSSFF();
- var buffer = Encoding.ASCII.GetBytes(time);
- fs.Write(buffer, 0, buffer.Length);
- if (addEndBytes)
- {
- fs.WriteByte(0xd);
- fs.WriteByte(0xa);
- }
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- if (!fileName.EndsWith(".cin", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- private static TimeCode DecodeTimestamp(string timeCode)
- {
- try
- {
- return new TimeCode(int.Parse(timeCode.Substring(0, 2)), int.Parse(timeCode.Substring(2, 2)), int.Parse(timeCode.Substring(4, 2)), FramesToMillisecondsMax999(int.Parse(timeCode.Substring(6, 2))));
- }
- catch (Exception exception)
- {
- System.Diagnostics.Debug.WriteLine(exception.Message);
- return new TimeCode();
- }
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- int i = 256;
- Paragraph last = null;
- while (i < buffer.Length - 20)
- {
- var p = new Paragraph();
-
- while (buffer[i] != 0xfe && i < buffer.Length - 20)
- {
- i++;
- }
- if (buffer[i] == 0xfe)
- {
- i += 4;
- string startTime = Encoding.ASCII.GetString(buffer, i, 8);
- i += 8;
- if (Utilities.IsInteger(startTime))
- {
- p.StartTime = DecodeTimestamp(startTime);
- }
- }
-
- bool startFound = false;
- bool textEnd = false;
- while (!startFound && !textEnd && i < buffer.Length - 20)
- {
- bool skip = false;
- if (buffer[i] == 0x0d)
- {
- i++;
- }
- else if (buffer[i] == 0x0a)
- {
- skip = true;
- }
- else if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
- {
- textEnd = true;
- }
- else if (buffer[i] <= 0x20) // text start
- {
- i++;
- }
- else
- {
- startFound = true;
- }
-
- if (!skip)
- {
- i++;
- }
- }
- i++;
-
- if (!textEnd)
- {
- i -= 2;
- var sb = new StringBuilder();
- while (!textEnd && i < buffer.Length - 20)
- {
- if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
- {
- textEnd = true;
- }
- else if (buffer[i] == 0xd && buffer[i + 1] == 0xa) // text end
- {
- textEnd = true;
- }
- else if (buffer[i] <= 0x17)
- {
- if (!sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
- {
- sb.Append(Environment.NewLine);
- }
-
- i++;
- }
- else
- {
- sb.Append(Encoding.GetEncoding(1252).GetString(buffer, i, 1));
- }
-
- i++;
- }
- i++;
- if (sb.Length > 0)
- {
- string text = sb.ToString().Trim();
- p.Text = text;
- subtitle.Paragraphs.Add(p);
- last = p;
- }
- }
-
- if (buffer[i] == 0xFE)
- {
- string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
- if (Utilities.IsInteger(endTime))
- {
- p.EndTime = DecodeTimestamp(endTime);
- }
- while (i < buffer.Length && buffer[i] != 0xa)
- {
- i++;
- }
-
- i++;
- }
- else
- {
- while (i < buffer.Length && buffer[i] != 0xa)
- {
- i++;
- }
-
- i++;
-
- if (buffer[i] == 0xfe)
- {
- string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
- if (Utilities.IsInteger(endTime))
- {
- p.EndTime = DecodeTimestamp(endTime);
- }
- }
- }
- }
- if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
- {
- last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
- }
-
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CaptionsInc : SubtitleFormat
+ {
+ public override string Extension => ".cin";
+
+ public override string Name => "Caption Inc";
+
+ public static void Save(string fileName, Subtitle subtitle)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ string name = Path.GetFileNameWithoutExtension(fileName);
+ byte[] buffer = Encoding.ASCII.GetBytes(name);
+ for (int i = 0; i < buffer.Length && i < 8; i++)
+ {
+ fs.WriteByte(buffer[i]);
+ }
+
+ while (fs.Length < 8)
+ {
+ fs.WriteByte(0x20);
+ }
+
+ WriteTime(fs, subtitle.Paragraphs[0].StartTime, false); // first start time
+ WriteTime(fs, subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime, false); // last end time
+
+ buffer = Encoding.ASCII.GetBytes("Generic Unknown Unknown \"\" Unknown Unknown Unknown".PadRight(230, ' '));
+ fs.Write(buffer, 0, buffer.Length);
+
+ // paragraphs
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ buffer = new byte[] { 0x0D, 0x0A, 0xFE }; // header
+ fs.Write(buffer, 0, buffer.Length);
+
+ // styles
+ var text = new List { 0x14, 0x20, 0x14, 0x2E, 0x14, 0x54, 0x17 };
+ int noOfLines = Utilities.GetNumberOfLines(p.Text);
+ text.Add(noOfLines == 1 ? (byte)0x22 : (byte)0x21);
+
+ var lines = p.Text.Split(Utilities.NewLineChars, StringSplitOptions.None);
+ foreach (string line in lines)
+ {
+ foreach (char ch in line)
+ {
+ text.Add(Encoding.GetEncoding(1252).GetBytes(new[] { ch })[0]);
+ }
+ text.Add(0x14);
+ text.Add(0x74);
+ }
+
+ // codes+text length
+ buffer = Encoding.ASCII.GetBytes($"{text.Count:000}");
+ fs.Write(buffer, 0, buffer.Length);
+
+ WriteTime(fs, p.StartTime, true);
+
+ // write codes + text
+ foreach (byte b in text)
+ {
+ fs.WriteByte(b);
+ }
+
+ buffer = new byte[] { 0x14, 0x2F, 0x0D, 0x0A, 0xFE, 0x30, 0x30, 0x32, 0x30 };
+ fs.Write(buffer, 0, buffer.Length);
+ WriteTime(fs, p.EndTime, true);
+ }
+ }
+ }
+
+ private static void WriteTime(FileStream fs, TimeCode timeCode, bool addEndBytes)
+ {
+ var time = timeCode.ToHHMMSSFF();
+ var buffer = Encoding.ASCII.GetBytes(time);
+ fs.Write(buffer, 0, buffer.Length);
+ if (addEndBytes)
+ {
+ fs.WriteByte(0xd);
+ fs.WriteByte(0xa);
+ }
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ if (!fileName.EndsWith(".cin", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ private static TimeCode DecodeTimestamp(string timeCode)
+ {
+ try
+ {
+ return new TimeCode(int.Parse(timeCode.Substring(0, 2)), int.Parse(timeCode.Substring(2, 2)), int.Parse(timeCode.Substring(4, 2)), FramesToMillisecondsMax999(int.Parse(timeCode.Substring(6, 2))));
+ }
+ catch (Exception exception)
+ {
+ System.Diagnostics.Debug.WriteLine(exception.Message);
+ return new TimeCode();
+ }
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ int i = 256;
+ Paragraph last = null;
+ while (i < buffer.Length - 20)
+ {
+ var p = new Paragraph();
+
+ while (buffer[i] != 0xfe && i < buffer.Length - 20)
+ {
+ i++;
+ }
+ if (buffer[i] == 0xfe)
+ {
+ i += 4;
+ string startTime = Encoding.ASCII.GetString(buffer, i, 8);
+ i += 8;
+ if (Utilities.IsInteger(startTime))
+ {
+ p.StartTime = DecodeTimestamp(startTime);
+ }
+ }
+
+ bool startFound = false;
+ bool textEnd = false;
+ while (!startFound && !textEnd && i < buffer.Length - 20)
+ {
+ bool skip = false;
+ if (buffer[i] == 0x0d)
+ {
+ i++;
+ }
+ else if (buffer[i] == 0x0a)
+ {
+ skip = true;
+ }
+ else if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
+ {
+ textEnd = true;
+ }
+ else if (buffer[i] <= 0x20) // text start
+ {
+ i++;
+ }
+ else
+ {
+ startFound = true;
+ }
+
+ if (!skip)
+ {
+ i++;
+ }
+ }
+ i++;
+
+ if (!textEnd)
+ {
+ i -= 2;
+ var sb = new StringBuilder();
+ while (!textEnd && i < buffer.Length - 20)
+ {
+ if (buffer[i] == 0x14 && buffer[i + 1] == 0x2c) // text end
+ {
+ textEnd = true;
+ }
+ else if (buffer[i] == 0xd && buffer[i + 1] == 0xa) // text end
+ {
+ textEnd = true;
+ }
+ else if (buffer[i] <= 0x17)
+ {
+ if (!sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
+ {
+ sb.Append(Environment.NewLine);
+ }
+
+ i++;
+ }
+ else
+ {
+ sb.Append(Encoding.GetEncoding(1252).GetString(buffer, i, 1));
+ }
+
+ i++;
+ }
+ i++;
+ if (sb.Length > 0)
+ {
+ string text = sb.ToString().Trim();
+ p.Text = text;
+ subtitle.Paragraphs.Add(p);
+ last = p;
+ }
+ }
+
+ if (buffer[i] == 0xFE)
+ {
+ string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
+ if (Utilities.IsInteger(endTime))
+ {
+ p.EndTime = DecodeTimestamp(endTime);
+ }
+ while (i < buffer.Length && buffer[i] != 0xa)
+ {
+ i++;
+ }
+
+ i++;
+ }
+ else
+ {
+ while (i < buffer.Length && buffer[i] != 0xa)
+ {
+ i++;
+ }
+
+ i++;
+
+ if (buffer[i] == 0xfe)
+ {
+ string endTime = Encoding.ASCII.GetString(buffer, i + 4, 8);
+ if (Utilities.IsInteger(endTime))
+ {
+ p.EndTime = DecodeTimestamp(endTime);
+ }
+ }
+ }
+ }
+ if (last != null && last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
+ {
+ last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/CaraokeXml.cs b/src/libse/SubtitleFormats/CaraokeXml.cs
similarity index 97%
rename from libse/SubtitleFormats/CaraokeXml.cs
rename to src/libse/SubtitleFormats/CaraokeXml.cs
index 3ecc28bce..714799d71 100644
--- a/libse/SubtitleFormats/CaraokeXml.cs
+++ b/src/libse/SubtitleFormats/CaraokeXml.cs
@@ -1,96 +1,96 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CaraokeXml : SubtitleFormat
- {
- public override string Extension => ".crk";
-
- public override string Name => "Caraoke Xml";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- var paragraph = xml.DocumentElement.SelectSingleNode("paragraph");
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode item = xml.CreateElement("item");
-
- var start = xml.CreateAttribute("tc1");
- start.InnerText = p.StartTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
- item.Attributes.Append(start);
-
- var end = xml.CreateAttribute("tc2");
- end.InnerText = p.EndTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
- item.Attributes.Append(end);
-
- var attr = xml.CreateAttribute("attr");
- attr.InnerText = string.Empty;
- item.Attributes.Append(attr);
-
- item.InnerText = p.Text;
-
- paragraph.AppendChild(item);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string xmlAsText = sb.ToString();
-
- if (!xmlAsText.Contains(" ".crk";
+
+ public override string Name => "Caraoke Xml";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ var paragraph = xml.DocumentElement.SelectSingleNode("paragraph");
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode item = xml.CreateElement("item");
+
+ var start = xml.CreateAttribute("tc1");
+ start.InnerText = p.StartTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
+ item.Attributes.Append(start);
+
+ var end = xml.CreateAttribute("tc2");
+ end.InnerText = p.EndTime.TotalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
+ item.Attributes.Append(end);
+
+ var attr = xml.CreateAttribute("attr");
+ attr.InnerText = string.Empty;
+ item.Attributes.Append(attr);
+
+ item.InnerText = p.Text;
+
+ paragraph.AppendChild(item);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string xmlAsText = sb.ToString();
+
+ if (!xmlAsText.Contains(" ArabicDictionary = new Dictionary
- {
- { 0x58, "م" },
- { 0x41, "ا" },
- { 0x4e, "ص" },
- { 0x42, "ب" },
- { 0x46, "ح" },
- { 0x57, "ل" },
- { 0x47, "خ" },
- { 0x5d, "ي" },
- { 0x4a, "ر" },
- { 0x2c, "،" },
- { 0x59, "ن" },
- { 0x5a, "و" },
- { 0x43, "ت" },
- { 0x1d, "-" },
- { 0x49, "ذ" },
- { 0x45, "ج" },
- { 0x5b, "ه" },
- { 0x56, "ك" },
- { 0x21, "؟" },
- { 0x4c, "س" },
- { 0x52, "ع" },
- { 0x5c, "ة" },
- { 0x5e, "ى" },
- { 0x61, "أ" },
- { 0x48, "د" },
- { 0x4d, "ش" },
- { 0x60, "ء" },
- { 0x68, "ﻷ" },
- { 0x54, "ف" },
- { 0x55, "ق" },
- { 0x22, "!" },
- { 0x67, "ﻻ" },
- { 0x66, "ؤ" },
- { 0x64, "آ" },
- { 0x50, "ط" },
- { 0x6a, "ﻵ" },
- { 0x4f, "ض" },
- { 0x6b, "ﺋ" },
- { 0x44, "ث" },
- { 0x51, "ظ" },
- { 0x53, "غ" },
- { 0x4b, "ز" },
- { 0x23, "\"" },
- { 0x6c, "ـ" },
- };
-
- private static readonly List HebrewCodes = new List
- {
- 0x40, // א
- 0x41, // ב
- 0x42, // ג
- 0x43, // ד
- 0x44, // ה
- 0x45, // ו
- 0x46, // ז
- 0x47, // ח
- 0x49, // י
- 0x4c, // ל
- 0x4d, // ם
- 0x4e, // מ
- 0x4f, // ן
- 0x50, // נ
- 0x51, // ס
- 0x52, // ע
- 0x54, // פ
- 0x56, // צ
- 0x57, // ק
- 0x58, // ר
- 0x59, // ש
- 0x5A, // ת
- 0x4b, // כ
- 0x4a, // ך
- 0x48, // ט
- 0x53, // ף
- 0x55, // ץ
-
- 0xB1, // "a"
- 0xB2, // "b"
- 0xB3, // "c"
- 0xB4, // "d"
- 0xB5, // "e"
- 0xB6, // "f"
- 0xB7, // "g"
- 0xB8, // "h"
- 0xB9, // "i"
- 0xBA, // "j"
- 0xBB, // "k"
- 0xBC, // "l"
- 0xBD, // "m"
- 0xBE, // "n"
- 0xBF, // "o"
- 0xC0, // "p"
- 0xC1, // "q"
- 0xC2, // "r"
- 0xC3, // "s"
- 0xC4, // "t"
- 0xC5, // "u"
- 0xC6, // "v"
- 0xC7, // "w"
- 0xC8, // "x"
- 0xC9, // "y"
- 0xCA, // "z"
-
- 0x91, // "A"
- 0xDB, // "B" -- weird
- 0x93, // "C"
- 0xDC, // "D" -- weird
- 0x95, // "E"
- 0x96, // "F"
- 0x97, // "G"
- 0xAB, // "H" -- weird
- 0x99, // "I"
- 0x9A, // "J"
- 0x9B, // "K"
- 0x9C, // "L"
- 0xDD, // "M"
- 0xDE, // "N"
- 0x9F, // "O"
- 0xA0, // "P"
- 0xA1, // "Q"
- 0xA2, // "R"
- 0xA3, // "S"
- 0xA4, // "T"
- 0xA5, // "U"
- 0xA6, // "V"
- 0xA7, // "W"
- 0xA8, // "X" - weird
- 0xA9, // "Y"
- 0xAA, // "Z" - weird
- };
-
- private static readonly List HebrewLetters = new List
- {
- "א",
- "ב",
- "ג",
- "ד",
- "ה",
- "ו",
- "ז",
- "ח",
- "י",
- "ל",
- "ם",
- "מ",
- "ן",
- "נ",
- "ס",
- "ע",
- "פ",
- "צ",
- "ק",
- "ר",
- "ש",
- "ת",
- "כ",
- "ך",
- "ט",
- "ף",
- "ץ",
-
- "a", // 0xB1
- "b", // 0xB2
- "c", // 0xB3
- "d", // 0xB4
- "e", // 0xB5
- "f", // 0xB6
- "g", // 0xB7
- "h", // 0xB8
- "i", // 0xB9
- "j", // 0xBA
- "k", // 0xBB
- "l", // 0xBC
- "m", // 0xBD
- "n", // 0xBE
- "o", // 0xBF
- "p", // 0xC0
- "q", // 0xC1
- "r", // 0xC2
- "s", // 0xC3
- "t", // 0xC4
- "u", // 0xC5
- "v", // 0xC6
- "w", // 0xC7
- "x", // 0xC8
- "y", // 0xC9
- "z", // 0xCA
-
- "A", // 0x91,
- "B", // 0xDB,
- "C", // 0x93,
- "D", // 0xDC,
- "E", // 0x95,
- "F", // 0x96,
- "G", // 0x97,
- "H", // 0xAB,
- "I", // 0x99,
- "J", // 0x9A,
- "K", // 0x9B,
- "L", // 0x9C,
- "M", // 0xDD,
- "N", // 0xDE,
- "O", // 0x9F,
- "P", // 0xA0,
- "Q", // 0xA1,
- "R", // 0xA2,
- "S", // 0xA3,
- "T", // 0xA4,
- "U", // 0xA5,
- "V", // 0xA6,
- "W", // 0xA7,
- "X", // 0xA8,
- "Y", // 0xA9,
- "Z", // 0xAA,
- };
-
- private static readonly List RussianCodes = new List
- {
- 0x42, // Б
- 0x45, // Е
- 0x5A, // З
- 0x56, // В
- 0x49, // И
- 0x4E, // Н
- 0x58, // Ы
- 0x51, // Я
- 0x56, // V
- 0x53, // С
- 0x72, // р
- 0x69, // и
- 0x71, // я
- 0x6E, // н
- 0x74, // т
- 0x5C, // Э
- 0x77, // ю
- 0x46, // Ф
- 0x5E, // Ч
- 0x44, // Д
- 0x62, // б
- 0x73, // с
- 0x75, // у
- 0x64, // д
- 0x60, // ж
- 0x6A, // й
- 0x6C, // л
- 0x47, // Г
- 0x78, // ы
- 0x7A, // з
- 0x7E, // ч
- 0x6D, // м
- 0x67, // г
- 0x79, // ь
- 0x70, // п
- 0x76, // в
- 0x55, // У
- 0x7D, // щ
- 0x66, // ф
- 0x7C, // э
- 0x7B, // ш
- 0x50, // П
- 0x52, // П
- 0x68, // П
- };
-
- private static readonly List RussianLetters = new List
- {
- "Б",
- "Е",
- "З",
- "В",
- "И",
- "Н",
- "Ы",
- "Я",
- "V",
- "С",
- "р",
- "и",
- "я",
- "н",
- "т",
- "Э",
- "ю",
- "Ф",
- "Ч",
- "Д",
- "б",
- "с",
- "у",
- "д",
- "ж",
- "й",
- "л",
- "Г",
- "ы",
- "з",
- "ч",
- "м",
- "г",
- "ь",
- "п",
- "в",
- "У",
- "щ",
- "ф",
- "э",
- "ш",
- "П",
- "Р",
- "х",
- };
-
- public override string Extension => ".890";
-
- public const string NameOfFormat = "Cavena 890";
-
- public override string Name => NameOfFormat;
-
- public override bool IsTimeBased => false;
-
- private int _languageIdLine1 = LanguageIdEnglish;
- private int _languageIdLine2 = LanguageIdEnglish;
-
- public bool Save(string fileName, Subtitle subtitle, bool batchMode = false)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- return Save(fileName, fs, subtitle, batchMode);
- }
- }
-
- public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
- {
- int russianCount = 0;
- char[] logoGrams = { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' };
- char[] russianChars = { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' };
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- if (p.Text.Contains(logoGrams))
- {
- _languageIdLine1 = LanguageIdChineseSimplified;
- _languageIdLine2 = LanguageIdChineseSimplified;
- break;
- }
- if (p.Text.Contains(russianChars))
- {
- russianCount++;
- if (russianCount > 10)
- {
- _languageIdLine1 = LanguageIdRussian;
- _languageIdLine2 = LanguageIdRussian; // or 0x09?
- break;
- }
- }
- }
-
- if (Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId > 0)
- {
- _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
- _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
- }
- else
- {
- var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
- switch (language)
- {
- case "he":
- _languageIdLine1 = LanguageIdHebrew;
- _languageIdLine2 = LanguageIdHebrew; // or 0x09
- break;
- case "ru":
- _languageIdLine1 = LanguageIdRussian;
- _languageIdLine2 = LanguageIdRussian; // or 0x09?
- break;
- case "zh":
- _languageIdLine1 = LanguageIdChineseSimplified;
- _languageIdLine2 = LanguageIdChineseSimplified;
- break;
- case "da":
- _languageIdLine1 = LanguageIdDanish;
- _languageIdLine2 = LanguageIdDanish;
- break;
- }
- }
-
- // prompt???
- //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0)
- // _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1;
- //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0)
- // _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2;
-
- // write file header (some fields are known, some are not...)
-
- stream.WriteByte(0); // ?
- stream.WriteByte(0); // ?
-
- // tape number (20 bytes)
- for (int i = 0; i < 20; i++)
- {
- stream.WriteByte(0);
- }
-
- // ?
- for (int i = 0; i < 18; i++)
- {
- stream.WriteByte(0);
- }
-
- // translated programme title (28 bytes)
- string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
- if (title.Length > 28)
- {
- title = title.Substring(0, 28);
- }
-
- if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Title) && Configuration.Settings.SubtitleSettings.CurrentCavena89Title.Length <= 28)
- {
- title = Configuration.Settings.SubtitleSettings.CurrentCavena89Title;
- }
-
- var buffer = Encoding.ASCII.GetBytes(title);
- stream.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 28 - buffer.Length; i++)
- {
- stream.WriteByte(0);
- }
-
- // translator (28 bytes)
- if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator) && Configuration.Settings.SubtitleSettings.CurrentCavena890Translator.Length <= 28)
- {
- buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator);
- stream.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 28 - buffer.Length; i++)
- {
- stream.WriteByte(0);
- }
- }
- else
- {
- for (int i = 0; i < 28; i++)
- {
- stream.WriteByte(0);
- }
- }
-
- // ?
- for (int i = 0; i < 9; i++)
- {
- stream.WriteByte(0);
- }
-
- // translated episode title (11 bytes)
- for (int i = 0; i < 11; i++)
- {
- stream.WriteByte(0);
- }
-
- // ?
- for (int i = 0; i < 18; i++)
- {
- stream.WriteByte(0);
- }
-
- // ? + language codes
- buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 };
- stream.Write(buffer, 0, buffer.Length);
-
- // comments (24 bytes)
- buffer = Encoding.ASCII.GetBytes("");
- if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment) && Configuration.Settings.SubtitleSettings.CurrentCavena89Comment.Length <= 24)
- {
- buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment);
- }
-
- stream.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 24 - buffer.Length; i++)
- {
- stream.WriteByte(0);
- }
-
- // ??
- buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 };
- stream.Write(buffer, 0, buffer.Length);
-
- // number of subtitles
- stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
- stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
-
- // write font - prefix with binary zeroes
- buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english...
- for (int i = 0; i < 14 - buffer.Length; i++)
- {
- stream.WriteByte(0);
- }
-
- stream.Write(buffer, 0, buffer.Length);
-
- // ?
- for (int i = 0; i < 13; i++)
- {
- stream.WriteByte(0);
- }
-
- // number of subtitles again
- stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
- stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
-
-
- // number of subtitles again again
- stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
- stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
-
- // ?
- for (int i = 0; i < 6; i++)
- {
- stream.WriteByte(0);
- }
-
- // original programme title (28 chars)
- if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle) && Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle.Length <= 28)
- {
- buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle);
- stream.Write(buffer, 0, buffer.Length);
- for (int i = 0; i < 28 - buffer.Length; i++)
- {
- stream.WriteByte(0);
- }
- }
- else
- {
- for (int i = 0; i < 28; i++)
- {
- stream.WriteByte(0);
- }
- }
-
- // write font (use same font id from line 1)
- buffer = GetFontBytesFromLanguageId(_languageIdLine1);
- stream.Write(buffer, 0, buffer.Length);
-
- // ?
- stream.WriteByte(0x3d);
- stream.WriteByte(0x8d);
-
- // start of message time
- string startOfMessage = "10:00:00:00";
- if (Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage != null &&
- Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage.Length == startOfMessage.Length)
- {
- startOfMessage = Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage;
- }
-
- buffer = Encoding.ASCII.GetBytes(startOfMessage);
- stream.Write(buffer, 0, buffer.Length);
-
- buffer = new byte[]
- {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44
- };
- stream.Write(buffer, 0, buffer.Length);
-
- for (int i = 0; i < 92; i++)
- {
- stream.WriteByte(0);
- }
-
- // paragraphs
- int number = 16;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- // number
- stream.WriteByte((byte)(number / 256));
- stream.WriteByte((byte)(number % 256));
-
- WriteTime(stream, p.StartTime);
- WriteTime(stream, p.EndTime);
-
- if (p.Text.StartsWith("{\\an1}"))
- {
- stream.WriteByte(0x50); // left
- }
- else if (p.Text.StartsWith("{\\an3}"))
- {
- stream.WriteByte(0x52); // left
- }
- else
- {
- stream.WriteByte(0x54); // center
- }
-
- buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment...
- stream.Write(buffer, 0, buffer.Length);
-
- bool hasBox = Utilities.RemoveSsaTags(p.Text).StartsWith("");
- var text = p.Text.Replace("", string.Empty).Replace("", string.Empty);
- text = HtmlUtil.RemoveOpenCloseTags(Utilities.RemoveSsaTags(text), HtmlUtil.TagBold, HtmlUtil.TagFont, HtmlUtil.TagBold);
- WriteText(stream, text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1, hasBox);
-
- number += 16;
- }
- return true;
- }
-
- private static byte[] GetFontBytesFromLanguageId(int languageId)
- {
- var buffer = Encoding.ASCII.GetBytes("HLV23N.V");
- if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
- {
- buffer = Encoding.ASCII.GetBytes("CCKM44.V");
- }
- else if (languageId == LanguageIdArabic)
- {
- buffer = Encoding.ASCII.GetBytes("ARA19N.V");
- }
- else if (languageId == LanguageIdRussian)
- {
- buffer = Encoding.ASCII.GetBytes("KYRIL4.V");
- }
- else if (languageId == LanguageIdHebrew)
- {
- buffer = Encoding.ASCII.GetBytes("HEBNOA.V");
- }
- else if (languageId == LanguageIdDanish)
- {
- buffer = Encoding.ASCII.GetBytes("VFONTL.V");
- }
-
- return buffer;
- }
-
- private static void WriteText(Stream fs, string text, bool isLast, int languageIdLine, bool useBox)
- {
- var lines = text.SplitToLines();
- if (lines.Count > 2)
- {
- lines = Utilities.AutoBreakLine(text).SplitToLines();
- }
-
- string line1 = string.Empty;
- string line2;
- if (lines.Count > 1)
- {
- line1 = lines[0];
- line2 = lines[1];
- }
- else
- {
- line2 = lines[0];
- }
-
- var buffer = GetTextAsBytes(line1, languageIdLine);
- fs.Write(buffer, 0, buffer.Length);
-
- buffer = new byte[] { 00, 00, 00, 00, 00, 00 };
- if (useBox)
- {
- buffer[3] = 0xa0;
- }
-
- fs.Write(buffer, 0, buffer.Length);
-
- buffer = GetTextAsBytes(line2, languageIdLine);
- fs.Write(buffer, 0, buffer.Length);
-
- buffer = new byte[] { 00, 00, 00, 00 };
- if (!isLast)
- {
- fs.Write(buffer, 0, buffer.Length);
- }
- }
-
- private static byte[] GetTextAsBytes(string text, int languageId)
- {
- var buffer = new byte[51];
- int skipCount = 0;
- for (int i = 0; i < buffer.Length; i++)
- {
- buffer[i] = 0x7F;
- }
-
- if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
- {
- for (int i = 0; i < buffer.Length; i++)
- {
- buffer[i] = 0;
- }
- }
- else if (languageId == LanguageIdHebrew)
- {
- text = Utilities.ReverseNumbers(text);
- if (!Configuration.Settings.General.RightToLeftMode)
- {
- text = Utilities.ReverseStartAndEndingForRightToLeft(text);
- }
- }
-
- var encoding = Encoding.Default;
- int index = 0;
-
- if (languageId == LanguageIdHebrew)
- {
- text = ReverseAnsi(text);
- }
-
- for (int i = 0; i < text.Length; i++)
- {
- var current = text[i];
- if (skipCount > 0)
- {
- skipCount--;
- }
- else if (languageId == LanguageIdHebrew)
- {
- int letterIndex = HebrewLetters.IndexOf(current.ToString(CultureInfo.InvariantCulture));
- if (letterIndex >= 0)
- {
- buffer[index] = (byte)HebrewCodes[letterIndex];
- }
- else if (i + 3 < text.Length && text.Substring(i, 3) == "")
- {
- buffer[index] = 0x88;
- skipCount = 2;
- }
- else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
- {
- buffer[index] = 0x98;
- skipCount = 2;
- }
- else
- {
- buffer[index] = encoding.GetBytes(new[] { current })[0];
- }
- index++;
- }
- else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
- {
- encoding = Encoding.GetEncoding(1201);
- if (index < 49)
- {
- if (i + 3 < text.Length && text.Substring(i, 3) == "")
- {
- buffer[index] = 0x88;
- skipCount = 2;
- }
- else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
- {
- buffer[index] = 0x98;
- skipCount = 3;
- }
- else
- {
- buffer[index] = encoding.GetBytes(new[] { current })[0];
- index++;
- }
- }
- }
- else
- {
- if (index < 50)
- {
- if (current == 'æ')
- {
- buffer[index] = 0x1B;
- }
- else if (current == 'ø')
- {
- buffer[index] = 0x1C;
- }
- else if (current == 'å')
- {
- buffer[index] = 0x1D;
- }
- else if (current == 'Æ')
- {
- buffer[index] = 0x5B;
- }
- else if (current == 'Ø')
- {
- buffer[index] = 0x5C;
- }
- else if (current == 'Å')
- {
- buffer[index] = 0x5D;
- }
-
- // ăĂ şŞ ţŢ (romanian)
- else if (current == 'ă')
- {
- AddTwo(buffer, ref index, 0x89, 0x61);
- }
- else if (current == 'Ă')
- {
- AddTwo(buffer, ref index, 0x89, 0x41);
- }
- else if (current == 'ş')
- {
- AddTwo(buffer, ref index, 0x87, 0x73);
- }
- else if (current == 'Ş')
- {
- AddTwo(buffer, ref index, 0x87, 0x53);
- }
- else if (current == 'ţ')
- {
- AddTwo(buffer, ref index, 0x87, 0x74);
- }
- else if (current == 'Ţ')
- {
- AddTwo(buffer, ref index, 0x87, 0x54);
- }
-
- // Next mapping of diacritics is reverse engineered,
- // and currently only maps characters from latin alphabets according to https://en.wikipedia.org/wiki/Latin_alphabets
-
- // capitals with accent grave
- else if (current == 'À')
- {
- AddTwo(buffer, ref index, 0x81, 0x41);
- }
- else if (current == 'È')
- {
- AddTwo(buffer, ref index, 0x81, 0x45);
- }
- else if (current == 'Ì')
- {
- AddTwo(buffer, ref index, 0x81, 0x49);
- }
- else if (current == 'Ò')
- {
- AddTwo(buffer, ref index, 0x81, 0x4F);
- }
- else if (current == 'Ù')
- {
- AddTwo(buffer, ref index, 0x81, 0x55);
- }
- else if (current == 'Ẁ')
- {
- AddTwo(buffer, ref index, 0x81, 0x57);
- }
-
- // lowercase with accent grave
- else if (current == 'à')
- {
- AddTwo(buffer, ref index, 0x81, 0x61);
- }
- else if (current == 'è')
- {
- AddTwo(buffer, ref index, 0x81, 0x65);
- }
- else if (current == 'ì')
- {
- AddTwo(buffer, ref index, 0x81, 0x69);
- }
- else if (current == 'ò')
- {
- AddTwo(buffer, ref index, 0x81, 0x6F);
- }
- else if (current == 'ù')
- {
- AddTwo(buffer, ref index, 0x81, 0x75);
- }
- else if (current == 'ẁ')
- {
- AddTwo(buffer, ref index, 0x81, 0x75);
- }
-
- // capitals with accent aigu
- else if (current == 'Á')
- {
- AddTwo(buffer, ref index, 0x82, 0x41);
- }
- else if (current == 'Ć')
- {
- AddTwo(buffer, ref index, 0x82, 0x43);
- }
- else if (current == 'É')
- {
- AddTwo(buffer, ref index, 0x82, 0x45);
- }
- else if (current == 'Í')
- {
- AddTwo(buffer, ref index, 0x82, 0x49);
- }
- else if (current == 'Ĺ')
- {
- AddTwo(buffer, ref index, 0x82, 0x4C);
- }
- else if (current == 'Ń')
- {
- AddTwo(buffer, ref index, 0x82, 0x4E);
- }
- else if (current == 'Ó')
- {
- AddTwo(buffer, ref index, 0x82, 0x4F);
- }
- else if (current == 'Ŕ')
- {
- AddTwo(buffer, ref index, 0x82, 0x52);
- }
- else if (current == 'Ś')
- {
- AddTwo(buffer, ref index, 0x82, 0x53);
- }
- else if (current == 'Ú')
- {
- AddTwo(buffer, ref index, 0x82, 0x55);
- }
- else if (current == 'Ẃ')
- {
- AddTwo(buffer, ref index, 0x82, 0x57);
- }
- else if (current == 'Ý')
- {
- AddTwo(buffer, ref index, 0x82, 0x59);
- }
- else if (current == 'Ź')
- {
- AddTwo(buffer, ref index, 0x82, 0x5A);
- }
-
- // lowercase with accent aigu
- else if (current == 'á')
- {
- AddTwo(buffer, ref index, 0x82, 0x61);
- }
- else if (current == 'ć')
- {
- AddTwo(buffer, ref index, 0x82, 0x63);
- }
- else if (current == 'é')
- {
- AddTwo(buffer, ref index, 0x82, 0x65);
- }
- else if (current == 'í')
- {
- AddTwo(buffer, ref index, 0x82, 0x69);
- }
- else if (current == 'ĺ')
- {
- AddTwo(buffer, ref index, 0x82, 0x6C);
- }
- else if (current == 'ń')
- {
- AddTwo(buffer, ref index, 0x82, 0x6E);
- }
- else if (current == 'ó')
- {
- AddTwo(buffer, ref index, 0x82, 0x6F);
- }
- else if (current == 'ŕ')
- {
- AddTwo(buffer, ref index, 0x82, 0x72);
- }
- else if (current == 'ś')
- {
- AddTwo(buffer, ref index, 0x82, 0x73);
- }
- else if (current == 'ú')
- {
- AddTwo(buffer, ref index, 0x82, 0x75);
- }
- else if (current == 'ẃ')
- {
- AddTwo(buffer, ref index, 0x82, 0x77);
- }
- else if (current == 'ý')
- {
- AddTwo(buffer, ref index, 0x82, 0x79);
- }
- else if (current == 'ź')
- {
- AddTwo(buffer, ref index, 0x82, 0x7A);
- }
-
- // capitals with accent circonflexe
- else if (current == 'Â')
- {
- AddTwo(buffer, ref index, 0x83, 0x41);
- }
- else if (current == 'Ĉ')
- {
- AddTwo(buffer, ref index, 0x83, 0x43);
- }
- else if (current == 'Ê')
- {
- AddTwo(buffer, ref index, 0x83, 0x45);
- }
- else if (current == 'Ĝ')
- {
- AddTwo(buffer, ref index, 0x83, 0x47);
- }
- else if (current == 'Ĥ')
- {
- AddTwo(buffer, ref index, 0x83, 0x48);
- }
- else if (current == 'Î')
- {
- AddTwo(buffer, ref index, 0x83, 0x49);
- }
- else if (current == 'Ĵ')
- {
- AddTwo(buffer, ref index, 0x83, 0x4A);
- }
- else if (current == 'Ô')
- {
- AddTwo(buffer, ref index, 0x83, 0x4F);
- }
- else if (current == 'Ŝ')
- {
- AddTwo(buffer, ref index, 0x83, 0x53);
- }
- else if (current == 'Û')
- {
- AddTwo(buffer, ref index, 0x83, 0x55);
- }
- else if (current == 'Ŵ')
- {
- AddTwo(buffer, ref index, 0x83, 0x57);
- }
- else if (current == 'Ŷ')
- {
- AddTwo(buffer, ref index, 0x83, 0x59);
- }
-
- // lowercase with accent circonflexe
- else if (current == 'â')
- {
- AddTwo(buffer, ref index, 0x83, 0x61);
- }
- else if (current == 'ĉ')
- {
- AddTwo(buffer, ref index, 0x83, 0x63);
- }
- else if (current == 'ê')
- {
- AddTwo(buffer, ref index, 0x83, 0x65);
- }
- else if (current == 'ĝ')
- {
- AddTwo(buffer, ref index, 0x83, 0x67);
- }
- else if (current == 'ĥ')
- {
- AddTwo(buffer, ref index, 0x83, 0x68);
- }
- else if (current == 'î')
- {
- AddTwo(buffer, ref index, 0x83, 0x69);
- }
- else if (current == 'ĵ')
- {
- AddTwo(buffer, ref index, 0x83, 0x6A);
- }
- else if (current == 'ô')
- {
- AddTwo(buffer, ref index, 0x83, 0x6F);
- }
- else if (current == 'ŝ')
- {
- AddTwo(buffer, ref index, 0x83, 0x73);
- }
- else if (current == 'û')
- {
- AddTwo(buffer, ref index, 0x83, 0x75);
- }
- else if (current == 'ŵ')
- {
- AddTwo(buffer, ref index, 0x83, 0x77);
- }
- else if (current == 'ŷ')
- {
- AddTwo(buffer, ref index, 0x83, 0x79);
- }
-
- // capitals with caron
- else if (current == 'Ǎ')
- {
- AddTwo(buffer, ref index, 0x84, 0x41);
- }
- else if (current == 'Č')
- {
- AddTwo(buffer, ref index, 0x84, 0x43);
- }
- else if (current == 'Ď')
- {
- AddTwo(buffer, ref index, 0x84, 0x44);
- }
- else if (current == 'Ě')
- {
- AddTwo(buffer, ref index, 0x84, 0x45);
- }
- else if (current == 'Ǧ')
- {
- AddTwo(buffer, ref index, 0x84, 0x47);
- }
- else if (current == 'Ǐ')
- {
- AddTwo(buffer, ref index, 0x84, 0x49);
- }
- else if (current == 'Ľ')
- {
- AddTwo(buffer, ref index, 0x84, 0x4C);
- }
- else if (current == 'Ň')
- {
- AddTwo(buffer, ref index, 0x84, 0x4E);
- }
- else if (current == 'Ř')
- {
- AddTwo(buffer, ref index, 0x84, 0x52);
- }
- else if (current == 'Š')
- {
- AddTwo(buffer, ref index, 0x84, 0x53);
- }
- else if (current == 'Ť')
- {
- AddTwo(buffer, ref index, 0x84, 0x54);
- }
- else if (current == 'Ž')
- {
- AddTwo(buffer, ref index, 0x84, 0x5A);
- }
-
- // lowercase with caron
- else if (current == 'ǎ')
- {
- AddTwo(buffer, ref index, 0x84, 0x61);
- }
- else if (current == 'č')
- {
- AddTwo(buffer, ref index, 0x84, 0x63);
- }
- else if (current == 'ď')
- {
- AddTwo(buffer, ref index, 0x84, 0x64);
- }
- else if (current == 'ě')
- {
- AddTwo(buffer, ref index, 0x84, 0x65);
- }
- else if (current == 'ǧ')
- {
- AddTwo(buffer, ref index, 0x84, 0x67);
- }
- else if (current == 'ǐ')
- {
- AddTwo(buffer, ref index, 0x84, 0x69);
- }
- else if (current == 'ľ')
- {
- AddTwo(buffer, ref index, 0x84, 0x6C);
- }
- else if (current == 'ň')
- {
- AddTwo(buffer, ref index, 0x84, 0x6E);
- }
- else if (current == 'ř')
- {
- AddTwo(buffer, ref index, 0x84, 0x72);
- }
- else if (current == 'š')
- {
- AddTwo(buffer, ref index, 0x84, 0x73);
- }
- else if (current == 'ť')
- {
- AddTwo(buffer, ref index, 0x84, 0x74);
- }
- else if (current == 'ž')
- {
- AddTwo(buffer, ref index, 0x84, 0x7A);
- }
-
- // capitals with tilde
- else if (current == 'Ã')
- {
- AddTwo(buffer, ref index, 0x85, 0x41);
- }
- else if (current == 'Ĩ')
- {
- AddTwo(buffer, ref index, 0x85, 0x49);
- }
- else if (current == 'Ñ')
- {
- AddTwo(buffer, ref index, 0x85, 0x4E);
- }
- else if (current == 'Õ')
- {
- AddTwo(buffer, ref index, 0x85, 0x4F);
- }
- else if (current == 'Ũ')
- {
- AddTwo(buffer, ref index, 0x85, 0x55);
- }
-
- // lowercase with tilde
- else if (current == 'ã')
- {
- AddTwo(buffer, ref index, 0x85, 0x61);
- }
- else if (current == 'ĩ')
- {
- AddTwo(buffer, ref index, 0x85, 0x69);
- }
- else if (current == 'ñ')
- {
- AddTwo(buffer, ref index, 0x85, 0x6E);
- }
- else if (current == 'õ')
- {
- AddTwo(buffer, ref index, 0x85, 0x6F);
- }
- else if (current == 'ũ')
- {
- AddTwo(buffer, ref index, 0x85, 0x75);
- }
-
- // capitals with trema
- else if (current == 'Ä')
- {
- AddTwo(buffer, ref index, 0x86, 0x41);
- }
- else if (current == 'Ë')
- {
- AddTwo(buffer, ref index, 0x86, 0x45);
- }
- else if (current == 'Ï')
- {
- AddTwo(buffer, ref index, 0x86, 0x49);
- }
- else if (current == 'Ö')
- {
- AddTwo(buffer, ref index, 0x86, 0x4F);
- }
- else if (current == 'Ü')
- {
- AddTwo(buffer, ref index, 0x86, 0x55);
- }
- else if (current == 'Ẅ')
- {
- AddTwo(buffer, ref index, 0x86, 0x57);
- }
- else if (current == 'Ÿ')
- {
- AddTwo(buffer, ref index, 0x86, 0x59);
- }
-
- // lowercase with trema
- else if (current == 'ä')
- {
- AddTwo(buffer, ref index, 0x86, 0x61);
- }
- else if (current == 'ë')
- {
- AddTwo(buffer, ref index, 0x86, 0x65);
- }
- else if (current == 'ï')
- {
- AddTwo(buffer, ref index, 0x86, 0x69);
- }
- else if (current == 'ö')
- {
- AddTwo(buffer, ref index, 0x86, 0x6F);
- }
- else if (current == 'ü')
- {
- AddTwo(buffer, ref index, 0x86, 0x75);
- }
- else if (current == 'ẅ')
- {
- AddTwo(buffer, ref index, 0x86, 0x77);
- }
- else if (current == 'ÿ')
- {
- AddTwo(buffer, ref index, 0x86, 0x79);
- }
- else if (i + 3 < text.Length && text.Substring(i, 3) == "")
- {
- buffer[index] = 0x88;
- skipCount = 2;
- }
- else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
- {
- buffer[index] = 0x98;
- skipCount = 3;
- }
- else
- {
- buffer[index] = encoding.GetBytes(new[] { current })[0];
- }
- index++;
- }
- }
- }
-
- return buffer;
- }
-
- private static string ReverseAnsi(string text)
- {
- var sb = new StringBuilder();
- var ansi = new StringBuilder();
- foreach (var ch in text)
- {
- if (ch > 255)
- {
- if (ansi.Length > 0)
- {
- sb.Append(Utilities.ReverseString(ansi.ToString()));
- ansi.Clear();
- }
- sb.Append(ch);
- }
- else
- {
- ansi.Append(ch);
- }
- }
- if (ansi.Length > 0)
- {
- sb.Append(Utilities.ReverseString(ansi.ToString()));
- }
-
- return sb.ToString();
- }
-
- private static void AddTwo(byte[] buffer, ref int index, byte b1, byte b2)
- {
- buffer[index] = b1;
- index++;
- buffer[index] = b2;
- }
-
- private static void WriteTime(Stream fs, TimeCode timeCode)
- {
- double totalMilliseconds = timeCode.TotalMilliseconds;
- int frames = (int)Math.Round(totalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate));
- fs.WriteByte((byte)(frames / 256 / 256));
- fs.WriteByte((byte)(frames / 256));
- fs.WriteByte((byte)(frames % 256));
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 512 && fi.Length < 1024000) // not too small or too big
- {
- if (!fileName.EndsWith(".890", StringComparison.Ordinal))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- const int textLength = 51;
-
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- _languageIdLine1 = buffer[146];
- if (_languageIdLine1 == 0)
- {
- _languageIdLine1 = LanguageIdEnglish;
- }
-
- Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 = _languageIdLine1;
-
- _languageIdLine2 = buffer[147];
- if (_languageIdLine2 == 0)
- {
- _languageIdLine2 = LanguageIdEnglish;
- }
-
- Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 = _languageIdLine2;
-
- var fontNameLine1 = Encoding.ASCII.GetString(buffer, 187, 6);
- var fontNameLine2 = Encoding.ASCII.GetString(buffer, 246, 6);
-
- // Hebrew
- if (_languageIdLine1 == LanguageIdHebrew || fontNameLine1 == "HEBNOA" || fontNameLine2 == "HEBNOA")
- {
- _languageIdLine1 = LanguageIdHebrew;
- _languageIdLine2 = LanguageIdHebrew;
- }
-
- // Arabic
- if (_languageIdLine2 == LanguageIdArabic || fontNameLine1 == "ARABIC")
- {
- _languageIdLine1 = LanguageIdArabic;
- _languageIdLine2 = LanguageIdArabic;
- }
-
- // Russian
- else if (_languageIdLine1 == LanguageIdRussian || fontNameLine1.StartsWith("KYRIL", StringComparison.Ordinal) || fontNameLine2.StartsWith("KYRIL", StringComparison.Ordinal))
- {
- _languageIdLine1 = LanguageIdRussian;
- _languageIdLine2 = LanguageIdRussian;
- }
-
- // Chinese
- else if (_languageIdLine1 == LanguageIdChineseSimplified)
- {
- _languageIdLine1 = LanguageIdChineseSimplified;
- _languageIdLine2 = LanguageIdChineseSimplified;
- }
- else if (_languageIdLine1 == LanguageIdChineseTraditional || fontNameLine1 == "CCKM44" || fontNameLine2 == "CCKM44")
- {
- _languageIdLine1 = LanguageIdChineseTraditional;
- _languageIdLine2 = LanguageIdChineseTraditional;
- }
-
- int i = 455;
- int lastNumber = -1;
- while (i < buffer.Length - 20)
- {
- int start = i - textLength;
-
- int number = buffer[start - 16] * 256 + buffer[start - 15];
-
- var p = new Paragraph();
- double startFrame = buffer[start - 14] * 256 * 256 + buffer[start - 13] * 256 + buffer[start - 12];
- double endFrame = buffer[start - 11] * 256 * 256 + buffer[start - 10] * 256 + buffer[start - 9];
-
- byte boxType = buffer[start + textLength + 3];
-
- string line1 = FixText(buffer, start, textLength, _languageIdLine1);
- string line2 = FixText(buffer, start + textLength + 6, textLength, _languageIdLine2);
-
- if (lastNumber == number)
- {
- p = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
- string temp = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
- if (temp.Length > 0)
- {
- p.Text = temp;
- }
- }
- else
- {
- subtitle.Paragraphs.Add(p);
- p.StartTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * startFrame;
- p.EndTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * endFrame;
- p.Text = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
- }
- if (boxType >= 0xa0 && boxType <= 0xa9 && !string.IsNullOrEmpty(p.Text)) // box
- {
- if (p.Text.StartsWith("{\\") && p.Text.Contains("}"))
- {
- p.Text = p.Text.Insert(p.Text.IndexOf('}', 3) + 1, "") + "";
- }
- else
- {
- p.Text = "" + p.Text + "";
- }
- }
-
- lastNumber = number;
-
- i += 128;
- }
-
- subtitle.Renumber();
- }
-
- private static string FixText(byte[] buffer, int start, int textLength, int languageId)
- {
- string text;
-
- if (languageId == LanguageIdRussian)
- {
- var encoding = Encoding.GetEncoding(1252);
- var sb = new StringBuilder();
- for (int i = 0; i < textLength; i++)
- {
- int b = buffer[start + i];
- int idx = RussianCodes.IndexOf(b);
- if (idx >= 0)
- {
- sb.Append(RussianLetters[idx]);
- }
- else
- {
- sb.Append(encoding.GetString(buffer, start + i, 1));
- }
- }
-
- text = sb.ToString();
-
- text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
- text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
- text = FixColors(text);
-
- if (text.Contains(""))
- {
- text = text.Replace("", "");
- }
-
- if (text.Contains("") && !text.Contains(""))
- {
- text += "";
- }
- }
- else if (languageId == LanguageIdHebrew) // (_language == "HEBNOA")
- {
- var encoding = Encoding.GetEncoding(1252);
- var sb = new StringBuilder();
- for (int i = 0; i < textLength; i++)
- {
- int b = buffer[start + i];
- int idx = HebrewCodes.IndexOf(b);
- if (idx >= 0)
- {
- sb.Append(HebrewLetters[idx]);
- }
- else
- {
- sb.Append(encoding.GetString(buffer, start + i, 1));
- }
- }
-
- text = sb.ToString();
-
- text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
- text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
- text = FixColors(text);
-
- text = ReverseAnsi(text);
- text = Utilities.ReverseStartAndEndingForRightToLeft(text);
- }
- else if (languageId == LanguageIdArabic)
- {
- var encoding = Encoding.GetEncoding(1252);
- var sb = new StringBuilder();
- for (int i = 0; i < textLength; i++)
- {
- int b = buffer[start + i];
- if (ArabicDictionary.TryGetValue(b, out var v))
- {
- sb.Append(v);
- }
- else if (b != 0x7F) // filler (decimal 127)
- {
- sb.Append(encoding.GetString(buffer, start + i, 1));
- }
- }
-
- text = sb.ToString();
- text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
- text = FixColors(text).Trim();
- }
- else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) // (_language == "CCKM44" || _language == "TVB000")
- {
- int index = start;
-
- while (textLength >= 1 && index + textLength < buffer.Length && (buffer[index + textLength - 1] == 0))
- {
- textLength--;
- }
-
- if (textLength > 0)
- {
- text = Encoding.GetEncoding(1201).GetString(buffer, index, textLength).Replace("\0", string.Empty);
- }
- else
- {
- text = string.Empty;
- }
-
- var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5")
- text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
- text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
- text = FixColors(text);
- text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "");
- text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "");
-
- if (text.Contains(""))
- {
- text = text.Replace("", "");
- }
-
- if (text.Contains("") && !text.Contains(""))
- {
- text += "";
- }
- }
- else
- {
- var encoding = Encoding.GetEncoding(1252);
- text = encoding.GetString(buffer, start, textLength).Replace("\0", string.Empty);
-
- text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
- text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
- text = FixColors(text);
-
- text = text.Replace(encoding.GetString(new byte[] { 0x1B }), "æ");
- text = text.Replace(encoding.GetString(new byte[] { 0x1C }), "ø");
- text = text.Replace(encoding.GetString(new byte[] { 0x1D }), "å");
- text = text.Replace(encoding.GetString(new byte[] { 0x1E }), "Æ");
- text = text.Replace(encoding.GetString(new byte[] { 0x1F }), "Ø");
-
- text = text.Replace(encoding.GetString(new byte[] { 0x5B }), "Æ");
- text = text.Replace(encoding.GetString(new byte[] { 0x5C }), "Ø");
- text = text.Replace(encoding.GetString(new byte[] { 0x5D }), "Å");
-
- // capitals with accent grave
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x41 }), "À");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x45 }), "È");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x49 }), "Ì");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x4f }), "Ò");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x55 }), "Ù");
-
- // lowercase with accent grave
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x61 }), "à");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "è");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x69 }), "ì");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x6F }), "ò");
- text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x75 }), "ù");
-
- // capitals with accent aigu
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x41 }), "Á");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x43 }), "Ć");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x45 }), "É");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x49 }), "Í");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4C }), "Ĺ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4E }), "Ń");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4F }), "Ó");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x52 }), "Ŕ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x53 }), "Ś");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x55 }), "Ú");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x57 }), "Ẃ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x59 }), "Ý");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x5A }), "Ź");
-
- // lowercase with accent aigu
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x61 }), "á");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x63 }), "ć");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x65 }), "é");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x69 }), "í");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6C }), "ĺ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6E }), "ń");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6F }), "ó");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x72 }), "ŕ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x73 }), "ś");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x75 }), "ú");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x77 }), "ẃ");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x79 }), "ý");
- text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x7A }), "ź");
-
- // capitals with accent circonflexe
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x41 }), "Â");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x43 }), "Ĉ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x45 }), "Ê");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x47 }), "Ĝ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x48 }), "Ĥ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x49 }), "Î");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x4A }), "Ĵ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x4F }), "Ô");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x53 }), "Ŝ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x55 }), "Û");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x57 }), "Ŵ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x59 }), "Ŷ");
-
- // lowercase with accent circonflexe
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x61 }), "â");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x63 }), "ĉ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x65 }), "ê");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x67 }), "ĝ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x68 }), "ĥ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x69 }), "î");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x6A }), "ĵ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x6F }), "ô");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x73 }), "ŝ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x75 }), "û");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x77 }), "ŵ");
- text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x79 }), "ŷ");
-
- // capitals with caron
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x41 }), "Ǎ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x43 }), "Č");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x44 }), "Ď");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x45 }), "Ě");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x47 }), "Ǧ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x49 }), "Ǐ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x4C }), "Ľ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x4E }), "Ň");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x52 }), "Ř");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x53 }), "Š");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x54 }), "Ť");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x5A }), "Ž");
-
- // lowercase with caron
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x61 }), "ǎ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x63 }), "č");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x64 }), "ď");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x65 }), "ě");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x67 }), "ǧ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x69 }), "ǐ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x6C }), "ľ");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x6E }), "ň");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x72 }), "ř");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x73 }), "š");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x74 }), "ť");
- text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x7A }), "ž");
-
- // capitals with tilde
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x41 }), "Ã");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x49 }), "Ĩ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x4E }), "Ñ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x4F }), "Õ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x55 }), "Ũ");
-
- // lowercase with tilde
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x61 }), "ã");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x69 }), "ĩ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x6E }), "ñ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x6F }), "õ");
- text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x75 }), "ũ");
-
- // capitals with trema
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x41 }), "Ä");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x45 }), "Ë");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x49 }), "Ï");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x4F }), "Ö");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x55 }), "Ü");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x59 }), "Ÿ");
-
- // lowercase with trema
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x61 }), "ä");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x65 }), "ë");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x69 }), "ï");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x6F }), "ö");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x75 }), "ü");
- text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x79 }), "ÿ");
-
- // with ring
- text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x61 }), "å");
- text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x41 }), "Å");
-
- text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "");
- text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "");
-
- // ăĂ şŞ ţŢ (romanian)
- text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x61 }), "ă");
- text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x41 }), "Ă");
- text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x73 }), "ş");
- text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x53 }), "Ş");
- text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x74 }), "ţ");
- text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x54 }), "Ţ");
-
- if (text.Contains(""))
- {
- text = text.Replace("", "");
- }
-
- if (text.Contains("") && !text.Contains(""))
- {
- text += "";
- }
- }
- return text;
- }
-
- private static string FixColors(string text)
- {
- Encoding encoding = Encoding.GetEncoding(1252);
- bool fontColorOn = false;
- var sb = new StringBuilder();
- for (int i = 0; i < text.Length; i++)
- {
- var s = text.Substring(i, 1);
- if (s == encoding.GetString(new byte[] { 0xf1 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // red
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf2 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // green
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf3 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // yellow
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf4 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // purple
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf5 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // magenta
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf6 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- sb.Append(""); // cyan
- fontColorOn = true;
- }
- else if (s == encoding.GetString(new byte[] { 0xf7 }))
- {
- if (fontColorOn)
- {
- sb.Append(""); // white
- fontColorOn = false;
- }
- }
- else if (s == encoding.GetString(new byte[] { 0xf8 }))
- {
- sb.Append(""); // orange
- fontColorOn = true;
- }
- else
- {
- sb.Append(s);
- }
- }
- if (fontColorOn)
- {
- sb.Append(""); // white
- }
- return sb.ToString();
- }
-
- }
-}
+using Nikse.SubtitleEdit.Core.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Cavena890 : SubtitleFormat, IBinaryPersistableSubtitle
+ {
+ public const int LanguageIdDanish = 0x07;
+ public const int LanguageIdEnglish = 0x09;
+ public const int LanguageIdRussian = 0x56;
+ public const int LanguageIdArabic = 0x80;
+ public const int LanguageIdHebrew = 0x8f;
+ public const int LanguageIdChineseTraditional = 0x90;
+ public const int LanguageIdChineseSimplified = 0x91;
+ public const int LanguageIdRomanian = 0x22;
+
+ private static readonly Dictionary ArabicDictionary = new Dictionary
+ {
+ { 0x58, "م" },
+ { 0x41, "ا" },
+ { 0x4e, "ص" },
+ { 0x42, "ب" },
+ { 0x46, "ح" },
+ { 0x57, "ل" },
+ { 0x47, "خ" },
+ { 0x5d, "ي" },
+ { 0x4a, "ر" },
+ { 0x2c, "،" },
+ { 0x59, "ن" },
+ { 0x5a, "و" },
+ { 0x43, "ت" },
+ { 0x1d, "-" },
+ { 0x49, "ذ" },
+ { 0x45, "ج" },
+ { 0x5b, "ه" },
+ { 0x56, "ك" },
+ { 0x21, "؟" },
+ { 0x4c, "س" },
+ { 0x52, "ع" },
+ { 0x5c, "ة" },
+ { 0x5e, "ى" },
+ { 0x61, "أ" },
+ { 0x48, "د" },
+ { 0x4d, "ش" },
+ { 0x60, "ء" },
+ { 0x68, "ﻷ" },
+ { 0x54, "ف" },
+ { 0x55, "ق" },
+ { 0x22, "!" },
+ { 0x67, "ﻻ" },
+ { 0x66, "ؤ" },
+ { 0x64, "آ" },
+ { 0x50, "ط" },
+ { 0x6a, "ﻵ" },
+ { 0x4f, "ض" },
+ { 0x6b, "ﺋ" },
+ { 0x44, "ث" },
+ { 0x51, "ظ" },
+ { 0x53, "غ" },
+ { 0x4b, "ز" },
+ { 0x23, "\"" },
+ { 0x6c, "ـ" },
+ };
+
+ private static readonly List HebrewCodes = new List
+ {
+ 0x40, // א
+ 0x41, // ב
+ 0x42, // ג
+ 0x43, // ד
+ 0x44, // ה
+ 0x45, // ו
+ 0x46, // ז
+ 0x47, // ח
+ 0x49, // י
+ 0x4c, // ל
+ 0x4d, // ם
+ 0x4e, // מ
+ 0x4f, // ן
+ 0x50, // נ
+ 0x51, // ס
+ 0x52, // ע
+ 0x54, // פ
+ 0x56, // צ
+ 0x57, // ק
+ 0x58, // ר
+ 0x59, // ש
+ 0x5A, // ת
+ 0x4b, // כ
+ 0x4a, // ך
+ 0x48, // ט
+ 0x53, // ף
+ 0x55, // ץ
+
+ 0xB1, // "a"
+ 0xB2, // "b"
+ 0xB3, // "c"
+ 0xB4, // "d"
+ 0xB5, // "e"
+ 0xB6, // "f"
+ 0xB7, // "g"
+ 0xB8, // "h"
+ 0xB9, // "i"
+ 0xBA, // "j"
+ 0xBB, // "k"
+ 0xBC, // "l"
+ 0xBD, // "m"
+ 0xBE, // "n"
+ 0xBF, // "o"
+ 0xC0, // "p"
+ 0xC1, // "q"
+ 0xC2, // "r"
+ 0xC3, // "s"
+ 0xC4, // "t"
+ 0xC5, // "u"
+ 0xC6, // "v"
+ 0xC7, // "w"
+ 0xC8, // "x"
+ 0xC9, // "y"
+ 0xCA, // "z"
+
+ 0x91, // "A"
+ 0xDB, // "B" -- weird
+ 0x93, // "C"
+ 0xDC, // "D" -- weird
+ 0x95, // "E"
+ 0x96, // "F"
+ 0x97, // "G"
+ 0xAB, // "H" -- weird
+ 0x99, // "I"
+ 0x9A, // "J"
+ 0x9B, // "K"
+ 0x9C, // "L"
+ 0xDD, // "M"
+ 0xDE, // "N"
+ 0x9F, // "O"
+ 0xA0, // "P"
+ 0xA1, // "Q"
+ 0xA2, // "R"
+ 0xA3, // "S"
+ 0xA4, // "T"
+ 0xA5, // "U"
+ 0xA6, // "V"
+ 0xA7, // "W"
+ 0xA8, // "X" - weird
+ 0xA9, // "Y"
+ 0xAA, // "Z" - weird
+ };
+
+ private static readonly List HebrewLetters = new List
+ {
+ "א",
+ "ב",
+ "ג",
+ "ד",
+ "ה",
+ "ו",
+ "ז",
+ "ח",
+ "י",
+ "ל",
+ "ם",
+ "מ",
+ "ן",
+ "נ",
+ "ס",
+ "ע",
+ "פ",
+ "צ",
+ "ק",
+ "ר",
+ "ש",
+ "ת",
+ "כ",
+ "ך",
+ "ט",
+ "ף",
+ "ץ",
+
+ "a", // 0xB1
+ "b", // 0xB2
+ "c", // 0xB3
+ "d", // 0xB4
+ "e", // 0xB5
+ "f", // 0xB6
+ "g", // 0xB7
+ "h", // 0xB8
+ "i", // 0xB9
+ "j", // 0xBA
+ "k", // 0xBB
+ "l", // 0xBC
+ "m", // 0xBD
+ "n", // 0xBE
+ "o", // 0xBF
+ "p", // 0xC0
+ "q", // 0xC1
+ "r", // 0xC2
+ "s", // 0xC3
+ "t", // 0xC4
+ "u", // 0xC5
+ "v", // 0xC6
+ "w", // 0xC7
+ "x", // 0xC8
+ "y", // 0xC9
+ "z", // 0xCA
+
+ "A", // 0x91,
+ "B", // 0xDB,
+ "C", // 0x93,
+ "D", // 0xDC,
+ "E", // 0x95,
+ "F", // 0x96,
+ "G", // 0x97,
+ "H", // 0xAB,
+ "I", // 0x99,
+ "J", // 0x9A,
+ "K", // 0x9B,
+ "L", // 0x9C,
+ "M", // 0xDD,
+ "N", // 0xDE,
+ "O", // 0x9F,
+ "P", // 0xA0,
+ "Q", // 0xA1,
+ "R", // 0xA2,
+ "S", // 0xA3,
+ "T", // 0xA4,
+ "U", // 0xA5,
+ "V", // 0xA6,
+ "W", // 0xA7,
+ "X", // 0xA8,
+ "Y", // 0xA9,
+ "Z", // 0xAA,
+ };
+
+ private static readonly List RussianCodes = new List
+ {
+ 0x42, // Б
+ 0x45, // Е
+ 0x5A, // З
+ 0x56, // В
+ 0x49, // И
+ 0x4E, // Н
+ 0x58, // Ы
+ 0x51, // Я
+ 0x56, // V
+ 0x53, // С
+ 0x72, // р
+ 0x69, // и
+ 0x71, // я
+ 0x6E, // н
+ 0x74, // т
+ 0x5C, // Э
+ 0x77, // ю
+ 0x46, // Ф
+ 0x5E, // Ч
+ 0x44, // Д
+ 0x62, // б
+ 0x73, // с
+ 0x75, // у
+ 0x64, // д
+ 0x60, // ж
+ 0x6A, // й
+ 0x6C, // л
+ 0x47, // Г
+ 0x78, // ы
+ 0x7A, // з
+ 0x7E, // ч
+ 0x6D, // м
+ 0x67, // г
+ 0x79, // ь
+ 0x70, // п
+ 0x76, // в
+ 0x55, // У
+ 0x7D, // щ
+ 0x66, // ф
+ 0x7C, // э
+ 0x7B, // ш
+ 0x50, // П
+ 0x52, // П
+ 0x68, // П
+ };
+
+ private static readonly List RussianLetters = new List
+ {
+ "Б",
+ "Е",
+ "З",
+ "В",
+ "И",
+ "Н",
+ "Ы",
+ "Я",
+ "V",
+ "С",
+ "р",
+ "и",
+ "я",
+ "н",
+ "т",
+ "Э",
+ "ю",
+ "Ф",
+ "Ч",
+ "Д",
+ "б",
+ "с",
+ "у",
+ "д",
+ "ж",
+ "й",
+ "л",
+ "Г",
+ "ы",
+ "з",
+ "ч",
+ "м",
+ "г",
+ "ь",
+ "п",
+ "в",
+ "У",
+ "щ",
+ "ф",
+ "э",
+ "ш",
+ "П",
+ "Р",
+ "х",
+ };
+
+ public override string Extension => ".890";
+
+ public const string NameOfFormat = "Cavena 890";
+
+ public override string Name => NameOfFormat;
+
+ public override bool IsTimeBased => false;
+
+ private int _languageIdLine1 = LanguageIdEnglish;
+ private int _languageIdLine2 = LanguageIdEnglish;
+
+ public bool Save(string fileName, Subtitle subtitle, bool batchMode = false)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ return Save(fileName, fs, subtitle, batchMode);
+ }
+ }
+
+ public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
+ {
+ int russianCount = 0;
+ char[] logoGrams = { '的', '是', '啊', '吧', '好', '吧', '亲', '爱', '的', '早', '上' };
+ char[] russianChars = { 'я', 'д', 'й', 'л', 'щ', 'ж', 'ц', 'ф', 'ы' };
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (p.Text.Contains(logoGrams))
+ {
+ _languageIdLine1 = LanguageIdChineseSimplified;
+ _languageIdLine2 = LanguageIdChineseSimplified;
+ break;
+ }
+ if (p.Text.Contains(russianChars))
+ {
+ russianCount++;
+ if (russianCount > 10)
+ {
+ _languageIdLine1 = LanguageIdRussian;
+ _languageIdLine2 = LanguageIdRussian; // or 0x09?
+ break;
+ }
+ }
+ }
+
+ if (Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId > 0)
+ {
+ _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
+ _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena89LanguageId;
+ }
+ else
+ {
+ var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
+ switch (language)
+ {
+ case "he":
+ _languageIdLine1 = LanguageIdHebrew;
+ _languageIdLine2 = LanguageIdHebrew; // or 0x09
+ break;
+ case "ru":
+ _languageIdLine1 = LanguageIdRussian;
+ _languageIdLine2 = LanguageIdRussian; // or 0x09?
+ break;
+ case "zh":
+ _languageIdLine1 = LanguageIdChineseSimplified;
+ _languageIdLine2 = LanguageIdChineseSimplified;
+ break;
+ case "da":
+ _languageIdLine1 = LanguageIdDanish;
+ _languageIdLine2 = LanguageIdDanish;
+ break;
+ }
+ }
+
+ // prompt???
+ //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 >= 0)
+ // _languageIdLine1 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1;
+ //if (Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 >= 0)
+ // _languageIdLine2 = Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2;
+
+ // write file header (some fields are known, some are not...)
+
+ stream.WriteByte(0); // ?
+ stream.WriteByte(0); // ?
+
+ // tape number (20 bytes)
+ for (int i = 0; i < 20; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // ?
+ for (int i = 0; i < 18; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // translated programme title (28 bytes)
+ string title = Path.GetFileNameWithoutExtension(fileName) ?? string.Empty;
+ if (title.Length > 28)
+ {
+ title = title.Substring(0, 28);
+ }
+
+ if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Title) && Configuration.Settings.SubtitleSettings.CurrentCavena89Title.Length <= 28)
+ {
+ title = Configuration.Settings.SubtitleSettings.CurrentCavena89Title;
+ }
+
+ var buffer = Encoding.ASCII.GetBytes(title);
+ stream.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 28 - buffer.Length; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // translator (28 bytes)
+ if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator) && Configuration.Settings.SubtitleSettings.CurrentCavena890Translator.Length <= 28)
+ {
+ buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890Translator);
+ stream.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 28 - buffer.Length; i++)
+ {
+ stream.WriteByte(0);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 28; i++)
+ {
+ stream.WriteByte(0);
+ }
+ }
+
+ // ?
+ for (int i = 0; i < 9; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // translated episode title (11 bytes)
+ for (int i = 0; i < 11; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // ?
+ for (int i = 0; i < 18; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // ? + language codes
+ buffer = new byte[] { 0xA0, 0x05, 0x04, 0x03, 0x06, 0x06, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, (byte)_languageIdLine1, (byte)_languageIdLine2 };
+ stream.Write(buffer, 0, buffer.Length);
+
+ // comments (24 bytes)
+ buffer = Encoding.ASCII.GetBytes("");
+ if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment) && Configuration.Settings.SubtitleSettings.CurrentCavena89Comment.Length <= 24)
+ {
+ buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena89Comment);
+ }
+
+ stream.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 24 - buffer.Length; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // ??
+ buffer = new byte[] { 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ stream.Write(buffer, 0, buffer.Length);
+
+ // number of subtitles
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
+
+ // write font - prefix with binary zeroes
+ buffer = GetFontBytesFromLanguageId(_languageIdLine1); // also TBX308VFONTL.V for english...
+ for (int i = 0; i < 14 - buffer.Length; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ stream.Write(buffer, 0, buffer.Length);
+
+ // ?
+ for (int i = 0; i < 13; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // number of subtitles again
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
+
+
+ // number of subtitles again again
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count % 256));
+ stream.WriteByte((byte)(subtitle.Paragraphs.Count / 256));
+
+ // ?
+ for (int i = 0; i < 6; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // original programme title (28 chars)
+ if (!string.IsNullOrWhiteSpace(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle) && Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle.Length <= 28)
+ {
+ buffer = Encoding.ASCII.GetBytes(Configuration.Settings.SubtitleSettings.CurrentCavena890riginalTitle);
+ stream.Write(buffer, 0, buffer.Length);
+ for (int i = 0; i < 28 - buffer.Length; i++)
+ {
+ stream.WriteByte(0);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 28; i++)
+ {
+ stream.WriteByte(0);
+ }
+ }
+
+ // write font (use same font id from line 1)
+ buffer = GetFontBytesFromLanguageId(_languageIdLine1);
+ stream.Write(buffer, 0, buffer.Length);
+
+ // ?
+ stream.WriteByte(0x3d);
+ stream.WriteByte(0x8d);
+
+ // start of message time
+ string startOfMessage = "10:00:00:00";
+ if (Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage != null &&
+ Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage.Length == startOfMessage.Length)
+ {
+ startOfMessage = Configuration.Settings.SubtitleSettings.Cavena890StartOfMessage;
+ }
+
+ buffer = Encoding.ASCII.GetBytes(startOfMessage);
+ stream.Write(buffer, 0, buffer.Length);
+
+ buffer = new byte[]
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x54, 0x44
+ };
+ stream.Write(buffer, 0, buffer.Length);
+
+ for (int i = 0; i < 92; i++)
+ {
+ stream.WriteByte(0);
+ }
+
+ // paragraphs
+ int number = 16;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ // number
+ stream.WriteByte((byte)(number / 256));
+ stream.WriteByte((byte)(number % 256));
+
+ WriteTime(stream, p.StartTime);
+ WriteTime(stream, p.EndTime);
+
+ if (p.Text.StartsWith("{\\an1}"))
+ {
+ stream.WriteByte(0x50); // left
+ }
+ else if (p.Text.StartsWith("{\\an3}"))
+ {
+ stream.WriteByte(0x52); // left
+ }
+ else
+ {
+ stream.WriteByte(0x54); // center
+ }
+
+ buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0 }; // 0x16 }; -- the last two bytes might be something with vertical alignment...
+ stream.Write(buffer, 0, buffer.Length);
+
+ bool hasBox = Utilities.RemoveSsaTags(p.Text).StartsWith("");
+ var text = p.Text.Replace("", string.Empty).Replace("", string.Empty);
+ text = HtmlUtil.RemoveOpenCloseTags(Utilities.RemoveSsaTags(text), HtmlUtil.TagBold, HtmlUtil.TagFont, HtmlUtil.TagBold);
+ WriteText(stream, text, p == subtitle.Paragraphs[subtitle.Paragraphs.Count - 1], _languageIdLine1, hasBox);
+
+ number += 16;
+ }
+ return true;
+ }
+
+ private static byte[] GetFontBytesFromLanguageId(int languageId)
+ {
+ var buffer = Encoding.ASCII.GetBytes("HLV23N.V");
+ if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
+ {
+ buffer = Encoding.ASCII.GetBytes("CCKM44.V");
+ }
+ else if (languageId == LanguageIdArabic)
+ {
+ buffer = Encoding.ASCII.GetBytes("ARA19N.V");
+ }
+ else if (languageId == LanguageIdRussian)
+ {
+ buffer = Encoding.ASCII.GetBytes("KYRIL4.V");
+ }
+ else if (languageId == LanguageIdHebrew)
+ {
+ buffer = Encoding.ASCII.GetBytes("HEBNOA.V");
+ }
+ else if (languageId == LanguageIdDanish)
+ {
+ buffer = Encoding.ASCII.GetBytes("VFONTL.V");
+ }
+
+ return buffer;
+ }
+
+ private static void WriteText(Stream fs, string text, bool isLast, int languageIdLine, bool useBox)
+ {
+ var lines = text.SplitToLines();
+ if (lines.Count > 2)
+ {
+ lines = Utilities.AutoBreakLine(text).SplitToLines();
+ }
+
+ string line1 = string.Empty;
+ string line2;
+ if (lines.Count > 1)
+ {
+ line1 = lines[0];
+ line2 = lines[1];
+ }
+ else
+ {
+ line2 = lines[0];
+ }
+
+ var buffer = GetTextAsBytes(line1, languageIdLine);
+ fs.Write(buffer, 0, buffer.Length);
+
+ buffer = new byte[] { 00, 00, 00, 00, 00, 00 };
+ if (useBox)
+ {
+ buffer[3] = 0xa0;
+ }
+
+ fs.Write(buffer, 0, buffer.Length);
+
+ buffer = GetTextAsBytes(line2, languageIdLine);
+ fs.Write(buffer, 0, buffer.Length);
+
+ buffer = new byte[] { 00, 00, 00, 00 };
+ if (!isLast)
+ {
+ fs.Write(buffer, 0, buffer.Length);
+ }
+ }
+
+ private static byte[] GetTextAsBytes(string text, int languageId)
+ {
+ var buffer = new byte[51];
+ int skipCount = 0;
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ buffer[i] = 0x7F;
+ }
+
+ if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
+ {
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ buffer[i] = 0;
+ }
+ }
+ else if (languageId == LanguageIdHebrew)
+ {
+ text = Utilities.ReverseNumbers(text);
+ if (!Configuration.Settings.General.RightToLeftMode)
+ {
+ text = Utilities.ReverseStartAndEndingForRightToLeft(text);
+ }
+ }
+
+ var encoding = Encoding.Default;
+ int index = 0;
+
+ if (languageId == LanguageIdHebrew)
+ {
+ text = ReverseAnsi(text);
+ }
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ var current = text[i];
+ if (skipCount > 0)
+ {
+ skipCount--;
+ }
+ else if (languageId == LanguageIdHebrew)
+ {
+ int letterIndex = HebrewLetters.IndexOf(current.ToString(CultureInfo.InvariantCulture));
+ if (letterIndex >= 0)
+ {
+ buffer[index] = (byte)HebrewCodes[letterIndex];
+ }
+ else if (i + 3 < text.Length && text.Substring(i, 3) == "")
+ {
+ buffer[index] = 0x88;
+ skipCount = 2;
+ }
+ else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
+ {
+ buffer[index] = 0x98;
+ skipCount = 2;
+ }
+ else
+ {
+ buffer[index] = encoding.GetBytes(new[] { current })[0];
+ }
+ index++;
+ }
+ else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified)
+ {
+ encoding = Encoding.GetEncoding(1201);
+ if (index < 49)
+ {
+ if (i + 3 < text.Length && text.Substring(i, 3) == "")
+ {
+ buffer[index] = 0x88;
+ skipCount = 2;
+ }
+ else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
+ {
+ buffer[index] = 0x98;
+ skipCount = 3;
+ }
+ else
+ {
+ buffer[index] = encoding.GetBytes(new[] { current })[0];
+ index++;
+ }
+ }
+ }
+ else
+ {
+ if (index < 50)
+ {
+ if (current == 'æ')
+ {
+ buffer[index] = 0x1B;
+ }
+ else if (current == 'ø')
+ {
+ buffer[index] = 0x1C;
+ }
+ else if (current == 'å')
+ {
+ buffer[index] = 0x1D;
+ }
+ else if (current == 'Æ')
+ {
+ buffer[index] = 0x5B;
+ }
+ else if (current == 'Ø')
+ {
+ buffer[index] = 0x5C;
+ }
+ else if (current == 'Å')
+ {
+ buffer[index] = 0x5D;
+ }
+
+ // ăĂ şŞ ţŢ (romanian)
+ else if (current == 'ă')
+ {
+ AddTwo(buffer, ref index, 0x89, 0x61);
+ }
+ else if (current == 'Ă')
+ {
+ AddTwo(buffer, ref index, 0x89, 0x41);
+ }
+ else if (current == 'ş')
+ {
+ AddTwo(buffer, ref index, 0x87, 0x73);
+ }
+ else if (current == 'Ş')
+ {
+ AddTwo(buffer, ref index, 0x87, 0x53);
+ }
+ else if (current == 'ţ')
+ {
+ AddTwo(buffer, ref index, 0x87, 0x74);
+ }
+ else if (current == 'Ţ')
+ {
+ AddTwo(buffer, ref index, 0x87, 0x54);
+ }
+
+ // Next mapping of diacritics is reverse engineered,
+ // and currently only maps characters from latin alphabets according to https://en.wikipedia.org/wiki/Latin_alphabets
+
+ // capitals with accent grave
+ else if (current == 'À')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x41);
+ }
+ else if (current == 'È')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x45);
+ }
+ else if (current == 'Ì')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x49);
+ }
+ else if (current == 'Ò')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x4F);
+ }
+ else if (current == 'Ù')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x55);
+ }
+ else if (current == 'Ẁ')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x57);
+ }
+
+ // lowercase with accent grave
+ else if (current == 'à')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x61);
+ }
+ else if (current == 'è')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x65);
+ }
+ else if (current == 'ì')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x69);
+ }
+ else if (current == 'ò')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x6F);
+ }
+ else if (current == 'ù')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x75);
+ }
+ else if (current == 'ẁ')
+ {
+ AddTwo(buffer, ref index, 0x81, 0x75);
+ }
+
+ // capitals with accent aigu
+ else if (current == 'Á')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x41);
+ }
+ else if (current == 'Ć')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x43);
+ }
+ else if (current == 'É')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x45);
+ }
+ else if (current == 'Í')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x49);
+ }
+ else if (current == 'Ĺ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x4C);
+ }
+ else if (current == 'Ń')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x4E);
+ }
+ else if (current == 'Ó')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x4F);
+ }
+ else if (current == 'Ŕ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x52);
+ }
+ else if (current == 'Ś')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x53);
+ }
+ else if (current == 'Ú')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x55);
+ }
+ else if (current == 'Ẃ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x57);
+ }
+ else if (current == 'Ý')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x59);
+ }
+ else if (current == 'Ź')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x5A);
+ }
+
+ // lowercase with accent aigu
+ else if (current == 'á')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x61);
+ }
+ else if (current == 'ć')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x63);
+ }
+ else if (current == 'é')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x65);
+ }
+ else if (current == 'í')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x69);
+ }
+ else if (current == 'ĺ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x6C);
+ }
+ else if (current == 'ń')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x6E);
+ }
+ else if (current == 'ó')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x6F);
+ }
+ else if (current == 'ŕ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x72);
+ }
+ else if (current == 'ś')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x73);
+ }
+ else if (current == 'ú')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x75);
+ }
+ else if (current == 'ẃ')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x77);
+ }
+ else if (current == 'ý')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x79);
+ }
+ else if (current == 'ź')
+ {
+ AddTwo(buffer, ref index, 0x82, 0x7A);
+ }
+
+ // capitals with accent circonflexe
+ else if (current == 'Â')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x41);
+ }
+ else if (current == 'Ĉ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x43);
+ }
+ else if (current == 'Ê')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x45);
+ }
+ else if (current == 'Ĝ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x47);
+ }
+ else if (current == 'Ĥ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x48);
+ }
+ else if (current == 'Î')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x49);
+ }
+ else if (current == 'Ĵ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x4A);
+ }
+ else if (current == 'Ô')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x4F);
+ }
+ else if (current == 'Ŝ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x53);
+ }
+ else if (current == 'Û')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x55);
+ }
+ else if (current == 'Ŵ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x57);
+ }
+ else if (current == 'Ŷ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x59);
+ }
+
+ // lowercase with accent circonflexe
+ else if (current == 'â')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x61);
+ }
+ else if (current == 'ĉ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x63);
+ }
+ else if (current == 'ê')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x65);
+ }
+ else if (current == 'ĝ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x67);
+ }
+ else if (current == 'ĥ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x68);
+ }
+ else if (current == 'î')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x69);
+ }
+ else if (current == 'ĵ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x6A);
+ }
+ else if (current == 'ô')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x6F);
+ }
+ else if (current == 'ŝ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x73);
+ }
+ else if (current == 'û')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x75);
+ }
+ else if (current == 'ŵ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x77);
+ }
+ else if (current == 'ŷ')
+ {
+ AddTwo(buffer, ref index, 0x83, 0x79);
+ }
+
+ // capitals with caron
+ else if (current == 'Ǎ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x41);
+ }
+ else if (current == 'Č')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x43);
+ }
+ else if (current == 'Ď')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x44);
+ }
+ else if (current == 'Ě')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x45);
+ }
+ else if (current == 'Ǧ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x47);
+ }
+ else if (current == 'Ǐ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x49);
+ }
+ else if (current == 'Ľ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x4C);
+ }
+ else if (current == 'Ň')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x4E);
+ }
+ else if (current == 'Ř')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x52);
+ }
+ else if (current == 'Š')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x53);
+ }
+ else if (current == 'Ť')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x54);
+ }
+ else if (current == 'Ž')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x5A);
+ }
+
+ // lowercase with caron
+ else if (current == 'ǎ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x61);
+ }
+ else if (current == 'č')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x63);
+ }
+ else if (current == 'ď')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x64);
+ }
+ else if (current == 'ě')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x65);
+ }
+ else if (current == 'ǧ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x67);
+ }
+ else if (current == 'ǐ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x69);
+ }
+ else if (current == 'ľ')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x6C);
+ }
+ else if (current == 'ň')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x6E);
+ }
+ else if (current == 'ř')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x72);
+ }
+ else if (current == 'š')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x73);
+ }
+ else if (current == 'ť')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x74);
+ }
+ else if (current == 'ž')
+ {
+ AddTwo(buffer, ref index, 0x84, 0x7A);
+ }
+
+ // capitals with tilde
+ else if (current == 'Ã')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x41);
+ }
+ else if (current == 'Ĩ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x49);
+ }
+ else if (current == 'Ñ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x4E);
+ }
+ else if (current == 'Õ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x4F);
+ }
+ else if (current == 'Ũ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x55);
+ }
+
+ // lowercase with tilde
+ else if (current == 'ã')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x61);
+ }
+ else if (current == 'ĩ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x69);
+ }
+ else if (current == 'ñ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x6E);
+ }
+ else if (current == 'õ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x6F);
+ }
+ else if (current == 'ũ')
+ {
+ AddTwo(buffer, ref index, 0x85, 0x75);
+ }
+
+ // capitals with trema
+ else if (current == 'Ä')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x41);
+ }
+ else if (current == 'Ë')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x45);
+ }
+ else if (current == 'Ï')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x49);
+ }
+ else if (current == 'Ö')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x4F);
+ }
+ else if (current == 'Ü')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x55);
+ }
+ else if (current == 'Ẅ')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x57);
+ }
+ else if (current == 'Ÿ')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x59);
+ }
+
+ // lowercase with trema
+ else if (current == 'ä')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x61);
+ }
+ else if (current == 'ë')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x65);
+ }
+ else if (current == 'ï')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x69);
+ }
+ else if (current == 'ö')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x6F);
+ }
+ else if (current == 'ü')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x75);
+ }
+ else if (current == 'ẅ')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x77);
+ }
+ else if (current == 'ÿ')
+ {
+ AddTwo(buffer, ref index, 0x86, 0x79);
+ }
+ else if (i + 3 < text.Length && text.Substring(i, 3) == "")
+ {
+ buffer[index] = 0x88;
+ skipCount = 2;
+ }
+ else if (i + 4 <= text.Length && text.Substring(i, 4) == "")
+ {
+ buffer[index] = 0x98;
+ skipCount = 3;
+ }
+ else
+ {
+ buffer[index] = encoding.GetBytes(new[] { current })[0];
+ }
+ index++;
+ }
+ }
+ }
+
+ return buffer;
+ }
+
+ private static string ReverseAnsi(string text)
+ {
+ var sb = new StringBuilder();
+ var ansi = new StringBuilder();
+ foreach (var ch in text)
+ {
+ if (ch > 255)
+ {
+ if (ansi.Length > 0)
+ {
+ sb.Append(Utilities.ReverseString(ansi.ToString()));
+ ansi.Clear();
+ }
+ sb.Append(ch);
+ }
+ else
+ {
+ ansi.Append(ch);
+ }
+ }
+ if (ansi.Length > 0)
+ {
+ sb.Append(Utilities.ReverseString(ansi.ToString()));
+ }
+
+ return sb.ToString();
+ }
+
+ private static void AddTwo(byte[] buffer, ref int index, byte b1, byte b2)
+ {
+ buffer[index] = b1;
+ index++;
+ buffer[index] = b2;
+ }
+
+ private static void WriteTime(Stream fs, TimeCode timeCode)
+ {
+ double totalMilliseconds = timeCode.TotalMilliseconds;
+ int frames = (int)Math.Round(totalMilliseconds / (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate));
+ fs.WriteByte((byte)(frames / 256 / 256));
+ fs.WriteByte((byte)(frames / 256));
+ fs.WriteByte((byte)(frames % 256));
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 512 && fi.Length < 1024000) // not too small or too big
+ {
+ if (!fileName.EndsWith(".890", StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ const int textLength = 51;
+
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ _languageIdLine1 = buffer[146];
+ if (_languageIdLine1 == 0)
+ {
+ _languageIdLine1 = LanguageIdEnglish;
+ }
+
+ Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine1 = _languageIdLine1;
+
+ _languageIdLine2 = buffer[147];
+ if (_languageIdLine2 == 0)
+ {
+ _languageIdLine2 = LanguageIdEnglish;
+ }
+
+ Configuration.Settings.SubtitleSettings.CurrentCavena890LanguageIdLine2 = _languageIdLine2;
+
+ var fontNameLine1 = Encoding.ASCII.GetString(buffer, 187, 6);
+ var fontNameLine2 = Encoding.ASCII.GetString(buffer, 246, 6);
+
+ // Hebrew
+ if (_languageIdLine1 == LanguageIdHebrew || fontNameLine1 == "HEBNOA" || fontNameLine2 == "HEBNOA")
+ {
+ _languageIdLine1 = LanguageIdHebrew;
+ _languageIdLine2 = LanguageIdHebrew;
+ }
+
+ // Arabic
+ if (_languageIdLine2 == LanguageIdArabic || fontNameLine1 == "ARABIC")
+ {
+ _languageIdLine1 = LanguageIdArabic;
+ _languageIdLine2 = LanguageIdArabic;
+ }
+
+ // Russian
+ else if (_languageIdLine1 == LanguageIdRussian || fontNameLine1.StartsWith("KYRIL", StringComparison.Ordinal) || fontNameLine2.StartsWith("KYRIL", StringComparison.Ordinal))
+ {
+ _languageIdLine1 = LanguageIdRussian;
+ _languageIdLine2 = LanguageIdRussian;
+ }
+
+ // Chinese
+ else if (_languageIdLine1 == LanguageIdChineseSimplified)
+ {
+ _languageIdLine1 = LanguageIdChineseSimplified;
+ _languageIdLine2 = LanguageIdChineseSimplified;
+ }
+ else if (_languageIdLine1 == LanguageIdChineseTraditional || fontNameLine1 == "CCKM44" || fontNameLine2 == "CCKM44")
+ {
+ _languageIdLine1 = LanguageIdChineseTraditional;
+ _languageIdLine2 = LanguageIdChineseTraditional;
+ }
+
+ int i = 455;
+ int lastNumber = -1;
+ while (i < buffer.Length - 20)
+ {
+ int start = i - textLength;
+
+ int number = buffer[start - 16] * 256 + buffer[start - 15];
+
+ var p = new Paragraph();
+ double startFrame = buffer[start - 14] * 256 * 256 + buffer[start - 13] * 256 + buffer[start - 12];
+ double endFrame = buffer[start - 11] * 256 * 256 + buffer[start - 10] * 256 + buffer[start - 9];
+
+ byte boxType = buffer[start + textLength + 3];
+
+ string line1 = FixText(buffer, start, textLength, _languageIdLine1);
+ string line2 = FixText(buffer, start + textLength + 6, textLength, _languageIdLine2);
+
+ if (lastNumber == number)
+ {
+ p = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
+ string temp = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
+ if (temp.Length > 0)
+ {
+ p.Text = temp;
+ }
+ }
+ else
+ {
+ subtitle.Paragraphs.Add(p);
+ p.StartTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * startFrame;
+ p.EndTime.TotalMilliseconds = (TimeCode.BaseUnit / Configuration.Settings.General.CurrentFrameRate) * endFrame;
+ p.Text = (line1.TrimEnd() + Environment.NewLine + line2).TrimEnd();
+ }
+ if (boxType >= 0xa0 && boxType <= 0xa9 && !string.IsNullOrEmpty(p.Text)) // box
+ {
+ if (p.Text.StartsWith("{\\") && p.Text.Contains("}"))
+ {
+ p.Text = p.Text.Insert(p.Text.IndexOf('}', 3) + 1, "") + "";
+ }
+ else
+ {
+ p.Text = "" + p.Text + "";
+ }
+ }
+
+ lastNumber = number;
+
+ i += 128;
+ }
+
+ subtitle.Renumber();
+ }
+
+ private static string FixText(byte[] buffer, int start, int textLength, int languageId)
+ {
+ string text;
+
+ if (languageId == LanguageIdRussian)
+ {
+ var encoding = Encoding.GetEncoding(1252);
+ var sb = new StringBuilder();
+ for (int i = 0; i < textLength; i++)
+ {
+ int b = buffer[start + i];
+ int idx = RussianCodes.IndexOf(b);
+ if (idx >= 0)
+ {
+ sb.Append(RussianLetters[idx]);
+ }
+ else
+ {
+ sb.Append(encoding.GetString(buffer, start + i, 1));
+ }
+ }
+
+ text = sb.ToString();
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
+ text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
+ text = FixColors(text);
+
+ if (text.Contains(""))
+ {
+ text = text.Replace("", "");
+ }
+
+ if (text.Contains("") && !text.Contains(""))
+ {
+ text += "";
+ }
+ }
+ else if (languageId == LanguageIdHebrew) // (_language == "HEBNOA")
+ {
+ var encoding = Encoding.GetEncoding(1252);
+ var sb = new StringBuilder();
+ for (int i = 0; i < textLength; i++)
+ {
+ int b = buffer[start + i];
+ int idx = HebrewCodes.IndexOf(b);
+ if (idx >= 0)
+ {
+ sb.Append(HebrewLetters[idx]);
+ }
+ else
+ {
+ sb.Append(encoding.GetString(buffer, start + i, 1));
+ }
+ }
+
+ text = sb.ToString();
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
+ text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
+ text = FixColors(text);
+
+ text = ReverseAnsi(text);
+ text = Utilities.ReverseStartAndEndingForRightToLeft(text);
+ }
+ else if (languageId == LanguageIdArabic)
+ {
+ var encoding = Encoding.GetEncoding(1252);
+ var sb = new StringBuilder();
+ for (int i = 0; i < textLength; i++)
+ {
+ int b = buffer[start + i];
+ if (ArabicDictionary.TryGetValue(b, out var v))
+ {
+ sb.Append(v);
+ }
+ else if (b != 0x7F) // filler (decimal 127)
+ {
+ sb.Append(encoding.GetString(buffer, start + i, 1));
+ }
+ }
+
+ text = sb.ToString();
+ text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
+ text = FixColors(text).Trim();
+ }
+ else if (languageId == LanguageIdChineseTraditional || languageId == LanguageIdChineseSimplified) // (_language == "CCKM44" || _language == "TVB000")
+ {
+ int index = start;
+
+ while (textLength >= 1 && index + textLength < buffer.Length && (buffer[index + textLength - 1] == 0))
+ {
+ textLength--;
+ }
+
+ if (textLength > 0)
+ {
+ text = Encoding.GetEncoding(1201).GetString(buffer, index, textLength).Replace("\0", string.Empty);
+ }
+ else
+ {
+ text = string.Empty;
+ }
+
+ var encoding = Encoding.Default; // which encoding?? Encoding.GetEncoding("ISO-8859-5")
+ text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
+ text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
+ text = FixColors(text);
+ text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "");
+ text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "");
+
+ if (text.Contains(""))
+ {
+ text = text.Replace("", "");
+ }
+
+ if (text.Contains("") && !text.Contains(""))
+ {
+ text += "";
+ }
+ }
+ else
+ {
+ var encoding = Encoding.GetEncoding(1252);
+ text = encoding.GetString(buffer, start, textLength).Replace("\0", string.Empty);
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x7F }), string.Empty); // Used to fill empty space upto 51 bytes
+ text = text.Replace(encoding.GetString(new byte[] { 0xBE }), string.Empty); // Unknown?
+ text = FixColors(text);
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x1B }), "æ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x1C }), "ø");
+ text = text.Replace(encoding.GetString(new byte[] { 0x1D }), "å");
+ text = text.Replace(encoding.GetString(new byte[] { 0x1E }), "Æ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x1F }), "Ø");
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x5B }), "Æ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x5C }), "Ø");
+ text = text.Replace(encoding.GetString(new byte[] { 0x5D }), "Å");
+
+ // capitals with accent grave
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x41 }), "À");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x45 }), "È");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x49 }), "Ì");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x4f }), "Ò");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x55 }), "Ù");
+
+ // lowercase with accent grave
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x61 }), "à");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x65 }), "è");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x69 }), "ì");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x6F }), "ò");
+ text = text.Replace(encoding.GetString(new byte[] { 0x81, 0x75 }), "ù");
+
+ // capitals with accent aigu
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x41 }), "Á");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x43 }), "Ć");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x45 }), "É");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x49 }), "Í");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4C }), "Ĺ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4E }), "Ń");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x4F }), "Ó");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x52 }), "Ŕ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x53 }), "Ś");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x55 }), "Ú");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x57 }), "Ẃ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x59 }), "Ý");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x5A }), "Ź");
+
+ // lowercase with accent aigu
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x61 }), "á");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x63 }), "ć");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x65 }), "é");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x69 }), "í");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6C }), "ĺ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6E }), "ń");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x6F }), "ó");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x72 }), "ŕ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x73 }), "ś");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x75 }), "ú");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x77 }), "ẃ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x79 }), "ý");
+ text = text.Replace(encoding.GetString(new byte[] { 0x82, 0x7A }), "ź");
+
+ // capitals with accent circonflexe
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x41 }), "Â");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x43 }), "Ĉ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x45 }), "Ê");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x47 }), "Ĝ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x48 }), "Ĥ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x49 }), "Î");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x4A }), "Ĵ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x4F }), "Ô");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x53 }), "Ŝ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x55 }), "Û");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x57 }), "Ŵ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x59 }), "Ŷ");
+
+ // lowercase with accent circonflexe
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x61 }), "â");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x63 }), "ĉ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x65 }), "ê");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x67 }), "ĝ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x68 }), "ĥ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x69 }), "î");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x6A }), "ĵ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x6F }), "ô");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x73 }), "ŝ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x75 }), "û");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x77 }), "ŵ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x83, 0x79 }), "ŷ");
+
+ // capitals with caron
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x41 }), "Ǎ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x43 }), "Č");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x44 }), "Ď");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x45 }), "Ě");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x47 }), "Ǧ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x49 }), "Ǐ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x4C }), "Ľ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x4E }), "Ň");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x52 }), "Ř");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x53 }), "Š");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x54 }), "Ť");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x5A }), "Ž");
+
+ // lowercase with caron
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x61 }), "ǎ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x63 }), "č");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x64 }), "ď");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x65 }), "ě");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x67 }), "ǧ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x69 }), "ǐ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x6C }), "ľ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x6E }), "ň");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x72 }), "ř");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x73 }), "š");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x74 }), "ť");
+ text = text.Replace(encoding.GetString(new byte[] { 0x84, 0x7A }), "ž");
+
+ // capitals with tilde
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x41 }), "Ã");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x49 }), "Ĩ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x4E }), "Ñ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x4F }), "Õ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x55 }), "Ũ");
+
+ // lowercase with tilde
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x61 }), "ã");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x69 }), "ĩ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x6E }), "ñ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x6F }), "õ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x85, 0x75 }), "ũ");
+
+ // capitals with trema
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x41 }), "Ä");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x45 }), "Ë");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x49 }), "Ï");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x4F }), "Ö");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x55 }), "Ü");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x59 }), "Ÿ");
+
+ // lowercase with trema
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x61 }), "ä");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x65 }), "ë");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x69 }), "ï");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x6F }), "ö");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x75 }), "ü");
+ text = text.Replace(encoding.GetString(new byte[] { 0x86, 0x79 }), "ÿ");
+
+ // with ring
+ text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x61 }), "å");
+ text = text.Replace(encoding.GetString(new byte[] { 0x8C, 0x41 }), "Å");
+
+ text = text.Replace(encoding.GetString(new byte[] { 0x88 }), "");
+ text = text.Replace(encoding.GetString(new byte[] { 0x98 }), "");
+
+ // ăĂ şŞ ţŢ (romanian)
+ text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x61 }), "ă");
+ text = text.Replace(encoding.GetString(new byte[] { 0x89, 0x41 }), "Ă");
+ text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x73 }), "ş");
+ text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x53 }), "Ş");
+ text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x74 }), "ţ");
+ text = text.Replace(encoding.GetString(new byte[] { 0x87, 0x54 }), "Ţ");
+
+ if (text.Contains(""))
+ {
+ text = text.Replace("", "");
+ }
+
+ if (text.Contains("") && !text.Contains(""))
+ {
+ text += "";
+ }
+ }
+ return text;
+ }
+
+ private static string FixColors(string text)
+ {
+ Encoding encoding = Encoding.GetEncoding(1252);
+ bool fontColorOn = false;
+ var sb = new StringBuilder();
+ for (int i = 0; i < text.Length; i++)
+ {
+ var s = text.Substring(i, 1);
+ if (s == encoding.GetString(new byte[] { 0xf1 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // red
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf2 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // green
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf3 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // yellow
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf4 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // purple
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf5 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // magenta
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf6 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ sb.Append(""); // cyan
+ fontColorOn = true;
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf7 }))
+ {
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ fontColorOn = false;
+ }
+ }
+ else if (s == encoding.GetString(new byte[] { 0xf8 }))
+ {
+ sb.Append(""); // orange
+ fontColorOn = true;
+ }
+ else
+ {
+ sb.Append(s);
+ }
+ }
+ if (fontColorOn)
+ {
+ sb.Append(""); // white
+ }
+ return sb.ToString();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/CheetahCaption.cs b/src/libse/SubtitleFormats/CheetahCaption.cs
similarity index 97%
rename from libse/SubtitleFormats/CheetahCaption.cs
rename to src/libse/SubtitleFormats/CheetahCaption.cs
index 642f4e5ac..03c334d3f 100644
--- a/libse/SubtitleFormats/CheetahCaption.cs
+++ b/src/libse/SubtitleFormats/CheetahCaption.cs
@@ -1,367 +1,367 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CheetahCaption : SubtitleFormat
- {
- private static readonly Dictionary DicCodeLatin = new Dictionary
- {
- [0x81] = '♪',
- [0x82] = 'á',
- [0x83] = 'é',
- [0x84] = 'í',
- [0x85] = 'ó',
- [0x86] = 'ú',
- [0x87] = 'â',
- [0x88] = 'ê',
- [0x89] = 'î',
- [0x8A] = 'ô',
- [0x8B] = 'û',
- [0x8C] = 'à',
- [0x8D] = 'è',
- [0x8E] = 'Ñ',
- [0x8F] = 'ñ',
- [0x90] = 'ç',
- [0x91] = '¢',
- [0x92] = '£',
- [0x93] = '¿',
- [0x94] = '½',
- [0x95] = '®',
- };
-
- public override string Extension => ".cap";
-
- public const string NameOfFormat = "Cheetah Caption";
-
- public override string Name => NameOfFormat;
-
- public static void Save(string fileName, Subtitle subtitle)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- byte[] buffer = { 0xEA, 0x22, 1, 0 }; // header
- fs.Write(buffer, 0, buffer.Length);
-
- int numberOfLines = subtitle.Paragraphs.Count;
- fs.WriteByte((byte)(numberOfLines % 256)); // paragraphs - low byte
- fs.WriteByte((byte)(numberOfLines / 256)); // paragraphs - high byte
-
- buffer = new byte[] { 9, 0xA8, 0xAF, 0x4F }; // ?
- fs.Write(buffer, 0, buffer.Length);
-
- for (int i = 0; i < 118; i++)
- {
- fs.WriteByte(0);
- }
-
- var dictionaryLatinCode = DicCodeLatin.ToLookup(pair => pair.Value, pair => pair.Key);
-
- // paragraphs
- for (int index = 0; index < subtitle.Paragraphs.Count; index++)
- {
- var p = subtitle.Paragraphs[index];
- var next = subtitle.GetParagraphOrDefault(index + 1);
- string text = p.Text;
-
- var bufferShort = new byte[]
- {
- 0,
- 0,
- 3, // justification, 1=left, 2=right, 3=center
- 0xE, //horizontal position, 1=top, F=bottom
- 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right
- };
-
- //styles + ?
- buffer = new byte[]
- {
- 0x12,
- 1,
- 0,
- 0,
- 0,
- 0,
- 3, // justification, 1=left, 2=right, 3=center
- 0xF, //horizontal position, 1=top, F=bottom
- 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right
- };
-
- //Normal : 12 01 00 00 00 00 03 0F 10
- //Right-top : 12 01 00 00 00 00 03 01 1C
- //Top : 12 01 00 00 00 00 03 01 10
- //Left-top : 12 01 00 00 00 00 03 01 05
- //Left : 12 01 00 00 00 00 03 0F 0A
- //Right : 12 01 00 00 00 00 03 0F 1E
- //Left : 12 03 00 00 00 00 03 0F 07
-
- if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- buffer[7] = 1; // align top (vertical)
- bufferShort[3] = 1; // align top (vertical)
- }
- else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
- {
- buffer[7] = 8; // center (vertical)
- bufferShort[3] = 8; // align top (vertical)
- }
-
- if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an1}", StringComparison.Ordinal))
- {
- buffer[8] = 2; // align left (horizontal)
- bufferShort[4] = 2; // align left (horizontal)
- }
- else if (text.StartsWith("{\\an9}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an3}", StringComparison.Ordinal))
- {
- buffer[8] = 0x1e; // align right (vertical)
- bufferShort[4] = 0x1e; // align right (vertical)
- }
-
- int startTag = text.IndexOf('}');
- if (text.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
- {
- text = text.Remove(0, startTag + 1);
- }
-
- var textBytes = new List();
- var italic = p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal);
- text = HtmlUtil.RemoveHtmlTags(text);
- int j = 0;
- if (italic)
- {
- textBytes.Add(0xd0);
- }
-
- var encoding = Encoding.GetEncoding(1252);
- while (j < text.Length)
- {
- if (text.Substring(j).StartsWith(Environment.NewLine, StringComparison.Ordinal))
- {
- j += Environment.NewLine.Length;
- textBytes.Add(0);
- textBytes.Add(0);
- textBytes.Add(0);
- textBytes.Add(0);
- if (italic)
- {
- textBytes.Add(0xd0);
- }
- }
- else
- {
- if (dictionaryLatinCode.Contains(text[j]))
- {
- textBytes.AddRange(dictionaryLatinCode[text[j]]);
- }
- else
- {
- textBytes.Add(encoding.GetBytes(new[] { text[j] })[0]);
- }
-
- j++;
- }
- }
-
- int length = textBytes.Count + 20;
- long end = fs.Position + length;
- if (Configuration.Settings.SubtitleSettings.CheetahCaptionAlwayWriteEndTime || next == null || next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds >= 1500)
- {
- fs.WriteByte((byte)length);
-
- fs.WriteByte(p.Text.Trim().Contains(Environment.NewLine) ? (byte)0x62 : (byte)0x61);
-
- WriteTime(fs, p.StartTime);
- WriteTime(fs, p.EndTime);
- fs.Write(buffer, 0, buffer.Length); // styles
- }
- else
- {
- length = textBytes.Count + 20 - (buffer.Length - bufferShort.Length);
- end = fs.Position + length;
- fs.WriteByte((byte)length);
-
- fs.WriteByte(p.Text.Trim().Contains(Environment.NewLine) ? (byte)0x42 : (byte)0x41);
-
- WriteTime(fs, p.StartTime);
- fs.WriteByte(2);
- fs.WriteByte(1);
- fs.WriteByte(0);
- fs.WriteByte(0);
- fs.Write(bufferShort, 0, bufferShort.Length); // styles
- }
-
- foreach (byte b in textBytes) // text
- {
- fs.WriteByte(b);
- }
-
- while (end > fs.Position)
- {
- fs.WriteByte(0);
- }
- }
- }
- }
-
- private static void WriteTime(Stream fs, TimeCode timeCode)
- {
- fs.WriteByte((byte)timeCode.Hours);
- fs.WriteByte((byte)timeCode.Minutes);
- fs.WriteByte((byte)timeCode.Seconds);
- fs.WriteByte((byte)MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds));
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big
- {
- if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- for (int i = 0; i < buffer.Length - 20; i++)
- {
- if (buffer[i + 0] == 0xEA &&
- buffer[i + 1] == 0x22 &&
- buffer[i + 2] <= 3)
- {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title) => "Not supported!";
-
- private static TimeCode DecodeTimestamp(byte[] buffer, int index)
- {
- return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3]));
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- int i = 128;
- Paragraph last = null;
- var sb = new StringBuilder();
- while (i < buffer.Length - 16)
- {
- var p = new Paragraph();
- int length = buffer[i];
-
- int usedBytes = 20;
-
- p.StartTime = DecodeTimestamp(buffer, i + 2);
- p.EndTime = DecodeTimestamp(buffer, i + 6);
- if (p.EndTime.Hours == 2 && p.EndTime.Minutes == 1 && p.EndTime.Seconds == 0 && p.EndTime.Milliseconds == 0 &&
- (p.Duration.TotalMilliseconds < 0 || p.Duration.TotalMilliseconds > 5000))
- {
- usedBytes = 20 - 4;
- }
-
- int textLength = length - usedBytes;
- int start = usedBytes - 1;
- for (int j = 0; j < 4; j++)
- {
- if (buffer[i + start - 1] > 0x10)
- {
- start--;
- textLength++;
- }
- }
-
- if (textLength > 0 && buffer.Length >= i + textLength)
- {
- if (last != null && last.EndTime.TotalMilliseconds > p.StartTime.TotalMilliseconds)
- {
- last.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
- }
-
- sb.Clear();
- int j = 0;
- bool italics = false;
- var encoding = Encoding.GetEncoding(1252);
- while (j < textLength)
- {
- int index = i + start + j;
- if (buffer[index] == 0)
- {
- if (italics)
- {
- sb.Append("");
- }
-
- italics = false;
- if (!sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
- {
- sb.AppendLine();
- }
- }
- else if (DicCodeLatin.ContainsKey(buffer[index]))
- {
- sb.Append(DicCodeLatin[buffer[index]]);
- }
- else if (buffer[index] >= 0xC0 || buffer[index] <= 0x14) // codes/styles?
- {
- if (buffer[index] == 0xd0) // italics
- {
- italics = true;
- sb.Append("");
- }
- }
- else
- {
- sb.Append(encoding.GetString(buffer, index, 1));
- }
- j++;
- }
- if (italics)
- {
- sb.Append("");
- }
-
- p.Text = sb.ToString().Trim();
- p.Text = p.Text.Replace("" + Environment.NewLine + "", Environment.NewLine);
-
- subtitle.Paragraphs.Add(p);
- last = p;
- }
- if (length == 0)
- {
- length++;
- }
-
- i += length;
- }
- if (last != null && (last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds || last.Duration.TotalMilliseconds < Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds))
- {
- last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
- }
-
- for (var index = 0; index < subtitle.Paragraphs.Count - 1; index++)
- {
- var current = subtitle.Paragraphs[index];
- var next = subtitle.Paragraphs[index + 1];
- if (current.EndTime.Hours == 2 && current.EndTime.Minutes == 1 && current.EndTime.Seconds == 0 && current.EndTime.Milliseconds == 0 &&
- (current.Duration.TotalMilliseconds < 0 || current.Duration.TotalMilliseconds > 5000))
- {
- current.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
- }
- }
-
- subtitle.Renumber();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CheetahCaption : SubtitleFormat
+ {
+ private static readonly Dictionary DicCodeLatin = new Dictionary
+ {
+ [0x81] = '♪',
+ [0x82] = 'á',
+ [0x83] = 'é',
+ [0x84] = 'í',
+ [0x85] = 'ó',
+ [0x86] = 'ú',
+ [0x87] = 'â',
+ [0x88] = 'ê',
+ [0x89] = 'î',
+ [0x8A] = 'ô',
+ [0x8B] = 'û',
+ [0x8C] = 'à',
+ [0x8D] = 'è',
+ [0x8E] = 'Ñ',
+ [0x8F] = 'ñ',
+ [0x90] = 'ç',
+ [0x91] = '¢',
+ [0x92] = '£',
+ [0x93] = '¿',
+ [0x94] = '½',
+ [0x95] = '®',
+ };
+
+ public override string Extension => ".cap";
+
+ public const string NameOfFormat = "Cheetah Caption";
+
+ public override string Name => NameOfFormat;
+
+ public static void Save(string fileName, Subtitle subtitle)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ byte[] buffer = { 0xEA, 0x22, 1, 0 }; // header
+ fs.Write(buffer, 0, buffer.Length);
+
+ int numberOfLines = subtitle.Paragraphs.Count;
+ fs.WriteByte((byte)(numberOfLines % 256)); // paragraphs - low byte
+ fs.WriteByte((byte)(numberOfLines / 256)); // paragraphs - high byte
+
+ buffer = new byte[] { 9, 0xA8, 0xAF, 0x4F }; // ?
+ fs.Write(buffer, 0, buffer.Length);
+
+ for (int i = 0; i < 118; i++)
+ {
+ fs.WriteByte(0);
+ }
+
+ var dictionaryLatinCode = DicCodeLatin.ToLookup(pair => pair.Value, pair => pair.Key);
+
+ // paragraphs
+ for (int index = 0; index < subtitle.Paragraphs.Count; index++)
+ {
+ var p = subtitle.Paragraphs[index];
+ var next = subtitle.GetParagraphOrDefault(index + 1);
+ string text = p.Text;
+
+ var bufferShort = new byte[]
+ {
+ 0,
+ 0,
+ 3, // justification, 1=left, 2=right, 3=center
+ 0xE, //horizontal position, 1=top, F=bottom
+ 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right
+ };
+
+ //styles + ?
+ buffer = new byte[]
+ {
+ 0x12,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 3, // justification, 1=left, 2=right, 3=center
+ 0xF, //horizontal position, 1=top, F=bottom
+ 0x10 //horizontal position, 3=left, 0x10=center, 0x19=right
+ };
+
+ //Normal : 12 01 00 00 00 00 03 0F 10
+ //Right-top : 12 01 00 00 00 00 03 01 1C
+ //Top : 12 01 00 00 00 00 03 01 10
+ //Left-top : 12 01 00 00 00 00 03 01 05
+ //Left : 12 01 00 00 00 00 03 0F 0A
+ //Right : 12 01 00 00 00 00 03 0F 1E
+ //Left : 12 03 00 00 00 00 03 0F 07
+
+ if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ buffer[7] = 1; // align top (vertical)
+ bufferShort[3] = 1; // align top (vertical)
+ }
+ else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
+ {
+ buffer[7] = 8; // center (vertical)
+ bufferShort[3] = 8; // align top (vertical)
+ }
+
+ if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an1}", StringComparison.Ordinal))
+ {
+ buffer[8] = 2; // align left (horizontal)
+ bufferShort[4] = 2; // align left (horizontal)
+ }
+ else if (text.StartsWith("{\\an9}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an3}", StringComparison.Ordinal))
+ {
+ buffer[8] = 0x1e; // align right (vertical)
+ bufferShort[4] = 0x1e; // align right (vertical)
+ }
+
+ int startTag = text.IndexOf('}');
+ if (text.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
+ {
+ text = text.Remove(0, startTag + 1);
+ }
+
+ var textBytes = new List();
+ var italic = p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal);
+ text = HtmlUtil.RemoveHtmlTags(text);
+ int j = 0;
+ if (italic)
+ {
+ textBytes.Add(0xd0);
+ }
+
+ var encoding = Encoding.GetEncoding(1252);
+ while (j < text.Length)
+ {
+ if (text.Substring(j).StartsWith(Environment.NewLine, StringComparison.Ordinal))
+ {
+ j += Environment.NewLine.Length;
+ textBytes.Add(0);
+ textBytes.Add(0);
+ textBytes.Add(0);
+ textBytes.Add(0);
+ if (italic)
+ {
+ textBytes.Add(0xd0);
+ }
+ }
+ else
+ {
+ if (dictionaryLatinCode.Contains(text[j]))
+ {
+ textBytes.AddRange(dictionaryLatinCode[text[j]]);
+ }
+ else
+ {
+ textBytes.Add(encoding.GetBytes(new[] { text[j] })[0]);
+ }
+
+ j++;
+ }
+ }
+
+ int length = textBytes.Count + 20;
+ long end = fs.Position + length;
+ if (Configuration.Settings.SubtitleSettings.CheetahCaptionAlwayWriteEndTime || next == null || next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds >= 1500)
+ {
+ fs.WriteByte((byte)length);
+
+ fs.WriteByte(p.Text.Trim().Contains(Environment.NewLine) ? (byte)0x62 : (byte)0x61);
+
+ WriteTime(fs, p.StartTime);
+ WriteTime(fs, p.EndTime);
+ fs.Write(buffer, 0, buffer.Length); // styles
+ }
+ else
+ {
+ length = textBytes.Count + 20 - (buffer.Length - bufferShort.Length);
+ end = fs.Position + length;
+ fs.WriteByte((byte)length);
+
+ fs.WriteByte(p.Text.Trim().Contains(Environment.NewLine) ? (byte)0x42 : (byte)0x41);
+
+ WriteTime(fs, p.StartTime);
+ fs.WriteByte(2);
+ fs.WriteByte(1);
+ fs.WriteByte(0);
+ fs.WriteByte(0);
+ fs.Write(bufferShort, 0, bufferShort.Length); // styles
+ }
+
+ foreach (byte b in textBytes) // text
+ {
+ fs.WriteByte(b);
+ }
+
+ while (end > fs.Position)
+ {
+ fs.WriteByte(0);
+ }
+ }
+ }
+ }
+
+ private static void WriteTime(Stream fs, TimeCode timeCode)
+ {
+ fs.WriteByte((byte)timeCode.Hours);
+ fs.WriteByte((byte)timeCode.Minutes);
+ fs.WriteByte((byte)timeCode.Seconds);
+ fs.WriteByte((byte)MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds));
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big
+ {
+ if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ for (int i = 0; i < buffer.Length - 20; i++)
+ {
+ if (buffer[i + 0] == 0xEA &&
+ buffer[i + 1] == 0x22 &&
+ buffer[i + 2] <= 3)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title) => "Not supported!";
+
+ private static TimeCode DecodeTimestamp(byte[] buffer, int index)
+ {
+ return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3]));
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ int i = 128;
+ Paragraph last = null;
+ var sb = new StringBuilder();
+ while (i < buffer.Length - 16)
+ {
+ var p = new Paragraph();
+ int length = buffer[i];
+
+ int usedBytes = 20;
+
+ p.StartTime = DecodeTimestamp(buffer, i + 2);
+ p.EndTime = DecodeTimestamp(buffer, i + 6);
+ if (p.EndTime.Hours == 2 && p.EndTime.Minutes == 1 && p.EndTime.Seconds == 0 && p.EndTime.Milliseconds == 0 &&
+ (p.Duration.TotalMilliseconds < 0 || p.Duration.TotalMilliseconds > 5000))
+ {
+ usedBytes = 20 - 4;
+ }
+
+ int textLength = length - usedBytes;
+ int start = usedBytes - 1;
+ for (int j = 0; j < 4; j++)
+ {
+ if (buffer[i + start - 1] > 0x10)
+ {
+ start--;
+ textLength++;
+ }
+ }
+
+ if (textLength > 0 && buffer.Length >= i + textLength)
+ {
+ if (last != null && last.EndTime.TotalMilliseconds > p.StartTime.TotalMilliseconds)
+ {
+ last.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
+ }
+
+ sb.Clear();
+ int j = 0;
+ bool italics = false;
+ var encoding = Encoding.GetEncoding(1252);
+ while (j < textLength)
+ {
+ int index = i + start + j;
+ if (buffer[index] == 0)
+ {
+ if (italics)
+ {
+ sb.Append("");
+ }
+
+ italics = false;
+ if (!sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
+ {
+ sb.AppendLine();
+ }
+ }
+ else if (DicCodeLatin.ContainsKey(buffer[index]))
+ {
+ sb.Append(DicCodeLatin[buffer[index]]);
+ }
+ else if (buffer[index] >= 0xC0 || buffer[index] <= 0x14) // codes/styles?
+ {
+ if (buffer[index] == 0xd0) // italics
+ {
+ italics = true;
+ sb.Append("");
+ }
+ }
+ else
+ {
+ sb.Append(encoding.GetString(buffer, index, 1));
+ }
+ j++;
+ }
+ if (italics)
+ {
+ sb.Append("");
+ }
+
+ p.Text = sb.ToString().Trim();
+ p.Text = p.Text.Replace("" + Environment.NewLine + "", Environment.NewLine);
+
+ subtitle.Paragraphs.Add(p);
+ last = p;
+ }
+ if (length == 0)
+ {
+ length++;
+ }
+
+ i += length;
+ }
+ if (last != null && (last.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds || last.Duration.TotalMilliseconds < Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds))
+ {
+ last.EndTime.TotalMilliseconds = last.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(last.Text);
+ }
+
+ for (var index = 0; index < subtitle.Paragraphs.Count - 1; index++)
+ {
+ var current = subtitle.Paragraphs[index];
+ var next = subtitle.Paragraphs[index + 1];
+ if (current.EndTime.Hours == 2 && current.EndTime.Minutes == 1 && current.EndTime.Seconds == 0 && current.EndTime.Milliseconds == 0 &&
+ (current.Duration.TotalMilliseconds < 0 || current.Duration.TotalMilliseconds > 5000))
+ {
+ current.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
+ }
+ }
+
+ subtitle.Renumber();
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/CheetahCaptionOld.cs b/src/libse/SubtitleFormats/CheetahCaptionOld.cs
similarity index 97%
rename from libse/SubtitleFormats/CheetahCaptionOld.cs
rename to src/libse/SubtitleFormats/CheetahCaptionOld.cs
index 3c4a3467a..7152e9bc7 100644
--- a/libse/SubtitleFormats/CheetahCaptionOld.cs
+++ b/src/libse/SubtitleFormats/CheetahCaptionOld.cs
@@ -1,99 +1,99 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class CheetahCaptionOld : SubtitleFormat
- {
-
- public override string Extension => ".cap";
-
- public const string NameOfFormat = "Cheetah Caption Old";
-
- public override string Name => NameOfFormat;
-
- public static void Save(string fileName, Subtitle subtitle)
- {
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big
- {
- if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- if (buffer[0] == 0xEA && buffer[1] == 0x10)
- {
- var subtitle = new Subtitle();
- LoadSubtitle(subtitle, lines, fileName);
- return subtitle.Paragraphs.Count > _errorCount || _errorCount > 25;
- }
- }
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- private static TimeCode DecodeTimestamp(byte[] buffer, int index)
- {
- return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3]));
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- int i = 0x80;
- var sb = new StringBuilder();
- while (i < buffer.Length - 0xb1)
- {
- if (buffer[i] > 30)
- {
- _errorCount++;
- }
- sb.AppendLine(Encoding.ASCII.GetString(buffer, i + 0x1a, 38).Replace("\0", string.Empty));
- sb.Append(Encoding.ASCII.GetString(buffer, i + 0x40, 38).Replace("\0", string.Empty));
- var p = new Paragraph(DecodeTimestamp(buffer, i), DecodeTimestamp(buffer, i + 4), sb.ToString().Trim());
- subtitle.Paragraphs.Add(p);
- sb.Clear();
- i += 0xb2;
- }
- for (int index = 0; index < subtitle.Paragraphs.Count; index++)
- {
- var paragraph = subtitle.Paragraphs[index];
- var next = subtitle.GetParagraphOrDefault(index + 1);
- if (paragraph.Duration.TotalSeconds > 100)
- {
- _errorCount++;
- }
- if (Math.Abs(paragraph.EndTime.TotalMilliseconds) < 0.1 || paragraph.Duration.TotalMilliseconds < 0.1)
- {
- if (next != null)
- {
- paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
- }
- if (next == null || paragraph.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
- {
- paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text);
- }
- }
- }
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class CheetahCaptionOld : SubtitleFormat
+ {
+
+ public override string Extension => ".cap";
+
+ public const string NameOfFormat = "Cheetah Caption Old";
+
+ public override string Name => NameOfFormat;
+
+ public static void Save(string fileName, Subtitle subtitle)
+ {
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 200 && fi.Length < 1024000) // not too small or too big
+ {
+ if (fileName.EndsWith(".cap", StringComparison.OrdinalIgnoreCase))
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ if (buffer[0] == 0xEA && buffer[1] == 0x10)
+ {
+ var subtitle = new Subtitle();
+ LoadSubtitle(subtitle, lines, fileName);
+ return subtitle.Paragraphs.Count > _errorCount || _errorCount > 25;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ private static TimeCode DecodeTimestamp(byte[] buffer, int index)
+ {
+ return new TimeCode(buffer[index], buffer[index + 1], buffer[index + 2], FramesToMillisecondsMax999(buffer[index + 3]));
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ int i = 0x80;
+ var sb = new StringBuilder();
+ while (i < buffer.Length - 0xb1)
+ {
+ if (buffer[i] > 30)
+ {
+ _errorCount++;
+ }
+ sb.AppendLine(Encoding.ASCII.GetString(buffer, i + 0x1a, 38).Replace("\0", string.Empty));
+ sb.Append(Encoding.ASCII.GetString(buffer, i + 0x40, 38).Replace("\0", string.Empty));
+ var p = new Paragraph(DecodeTimestamp(buffer, i), DecodeTimestamp(buffer, i + 4), sb.ToString().Trim());
+ subtitle.Paragraphs.Add(p);
+ sb.Clear();
+ i += 0xb2;
+ }
+ for (int index = 0; index < subtitle.Paragraphs.Count; index++)
+ {
+ var paragraph = subtitle.Paragraphs[index];
+ var next = subtitle.GetParagraphOrDefault(index + 1);
+ if (paragraph.Duration.TotalSeconds > 100)
+ {
+ _errorCount++;
+ }
+ if (Math.Abs(paragraph.EndTime.TotalMilliseconds) < 0.1 || paragraph.Duration.TotalMilliseconds < 0.1)
+ {
+ if (next != null)
+ {
+ paragraph.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
+ }
+ if (next == null || paragraph.Duration.TotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds)
+ {
+ paragraph.EndTime.TotalMilliseconds = paragraph.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(paragraph.Text);
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Chk.cs b/src/libse/SubtitleFormats/Chk.cs
similarity index 97%
rename from libse/SubtitleFormats/Chk.cs
rename to src/libse/SubtitleFormats/Chk.cs
index 881cb5514..b2f2dd765 100644
--- a/libse/SubtitleFormats/Chk.cs
+++ b/src/libse/SubtitleFormats/Chk.cs
@@ -1,313 +1,313 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- ///
- /// .CHK subtitle file format - 128 bytes blocks, first byte in block is id (01==text)
- ///
- public class Chk : SubtitleFormat
- {
- private readonly Encoding _codePage = Encoding.GetEncoding(850);
- // private string _languageId = "DEN"; // English
-
- public override string Extension => ".chk";
-
- public const string NameOfFormat = "CHK";
-
- public override string Name => NameOfFormat;
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName.EndsWith(".chk", StringComparison.OrdinalIgnoreCase))
- {
- var buffer = FileUtil.ReadAllBytesShared(fileName);
- return buffer.Length > 0 && buffer[0] == 0x1d;
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not implemented!";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- var buffer = FileUtil.ReadAllBytesShared(fileName);
- int index = 256;
- _errorCount = 0;
- subtitle.Paragraphs.Clear();
- while (index < buffer.Length)
- {
- Paragraph p = ReadParagraph(buffer, index);
- if (p != null)
- {
- subtitle.Paragraphs.Add(p);
- }
-
- index += 128;
- }
-
- if (subtitle.Paragraphs.Count > 1)
- {
- if (string.IsNullOrWhiteSpace(subtitle.Paragraphs[0].Text))
- {
- subtitle.Paragraphs.RemoveAt(0);
- }
-
- if (string.IsNullOrWhiteSpace(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].Text))
- {
- subtitle.Paragraphs.RemoveAt(subtitle.Paragraphs.Count - 1);
- }
- }
-
- subtitle.Renumber();
- }
-
- private Queue _timeCodeQueue = new Queue();
-
- private Paragraph ReadParagraph(byte[] buffer, int index)
- {
- if (buffer[index] == 1 && _timeCodeQueue.Count > 0) // text
- {
- var sb = new StringBuilder();
- int skipCount = 0;
- int textLength = buffer[index + 2] - 11;
- int start = index + 13;
-
- for (int i = 0; i <= textLength; i++)
- {
- if (skipCount > 0)
- {
- skipCount--;
- }
- else if (buffer[index + 13 + i] == 0xFE)
- {
- sb.Append(GetText(buffer, start, index + i + 13));
- start = index + 13 + i + 3;
- skipCount = 2;
- sb.AppendLine();
- }
- else if (buffer[index + 13 + i] == 0)
- {
- sb.Append(GetText(buffer, start, index + i + 13));
- break;
- }
- if (i == textLength)
- {
- sb.Append(GetText(buffer, start, index + i + 13 + 1));
- }
- }
-
- Paragraph p;
- if (_timeCodeQueue.Count > 0)
- {
- p = _timeCodeQueue.Dequeue();
- }
- else
- {
- p = new Paragraph();
- }
-
- p.Number = buffer[index + 3] * 256 + buffer[index + 4]; // Subtitle number
- p.Text = sb.ToString();
- //if (p.Number == 0 && p.Text.StartsWith("LANG:", StringComparison.Ordinal) && p.Text.Length > 8)
- //{
- // _languageId = p.Text.Substring(5, 3);
- //}
- return p;
- }
-
- if (buffer[index] == 0x0a && _timeCodeQueue.Count > 0)
- {
- // ?
- }
- else if (buffer[index] == 0x09 && _timeCodeQueue.Count > 0)
- {
- // ?
- }
- else // time codes
- {
- var newTimeCodes = new List();
- for (int i = 0; i < 15; i++)
- {
- int start = index + 2 + (i * 8);
- int totalFrameNumber = (buffer[start + 3] << 16) + (buffer[start + 5] << 8) + buffer[start + 4];
- int durationInFrames = buffer[start + 6];
- var p = new Paragraph(string.Empty, FramesToMilliseconds(totalFrameNumber), FramesToMilliseconds(totalFrameNumber + durationInFrames));
- newTimeCodes.Add(p);
- }
-
- if (_timeCodeQueue.Count != 15 || IsSequential(newTimeCodes))
- {
- _timeCodeQueue = new Queue(newTimeCodes);
- }
- }
- return null;
- }
-
- private static bool IsSequential(List newTimeCodes)
- {
- var lastMs = 0.0;
- foreach (var p in newTimeCodes)
- {
- if (p.StartTime.TotalMilliseconds < lastMs)
- {
- return false;
- }
- lastMs = p.StartTime.TotalMilliseconds;
- }
- return true;
- }
-
- private string GetText(byte[] buffer, int start, int end)
- {
- string text = string.Empty;
- if (buffer[start] == 0x1f && buffer[start + 1] == 0x57 && buffer[start + 2] == 0x31 && buffer[start + 3] == 0x36) // W16
- {
- if (end - start > 4)
- {
- text = Encoding.GetEncoding(950).GetString(buffer, start + 4, end - start - 4);
- }
- }
- else
- {
- if (end - start > 0)
- {
- text = _codePage.GetString(buffer, start, end - start);
- }
- }
- if (text.Length > 4 && text[0] == 0x1f && text[1] == 'R' && text[4] == '.' && CharUtils.IsDigit(text[2]) && CharUtils.IsDigit(text[3]))
- {
- text = text.Remove(0, 5);
- }
-
- // special language codes...
- text = text.Replace("ÔA", "Á");
- text = text.Replace("ÔE", "É");
- text = text.Replace("ÔI", "Í");
- text = text.Replace("ÓN", "Ñ");
- text = text.Replace("ÔO", "Ó");
- text = text.Replace("ÔU", "Ú");
- text = text.Replace("Ôa", "á");
- text = text.Replace("Ôe", "é");
- text = text.Replace("Ôi", "í");
- text = text.Replace("Ón", "ñ");
- text = text.Replace("Ôo", "ó");
- text = text.Replace("Ôu", "ú");
-
- text = text.Replace("ÒA", "À");
- text = text.Replace("ÒE", "È");
- text = text.Replace("ÒU", "Ù");
- text = text.Replace("Òa", "à");
- text = text.Replace("Òe", "è");
- text = text.Replace("Òu", "ù");
-
- text = text.Replace("ÕU", "Ü");
- text = text.Replace("ÕA", "Ä");
- text = text.Replace("ÕO", "Ö");
- text = text.Replace("Õu", "ü");
- text = text.Replace("Õa", "ä");
- text = text.Replace("Õo", "ö");
-
- text = text.Replace("õa", "â");
- text = text.Replace("õe", "ê");
- text = text.Replace("õi", "î");
- text = text.Replace("õu", "û");
- text = text.Replace("õA", "Â");
- text = text.Replace("õE", "Ê");
- text = text.Replace("õI", "Î");
- text = text.Replace("õU", "Û");
-
- return ApplyFont(text);
- }
-
- private static string ApplyFont(string text)
- {
- var sb = new StringBuilder();
- string post = string.Empty;
- int i = 0;
- while (i < text.Length)
- {
- if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 0x1D && text[i + 2] == 07)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- i += 2;
- }
- else if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 06)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- i++;
- }
- else if (text[i] == 2)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- }
- else if (text[i] == 3)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- }
- else if (text[i] == 6)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- }
- else if (text[i] == 7)
- {
- if (post != string.Empty)
- {
- sb.Append("");
- }
-
- sb.Append("");
- post = "";
- }
- else
- {
- sb.Append(text[i]);
- }
- i++;
- }
-
- text = sb + post;
- if (string.IsNullOrWhiteSpace(HtmlUtil.RemoveHtmlTags(text)))
- {
- return string.Empty;
- }
-
- return text;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ ///
+ /// .CHK subtitle file format - 128 bytes blocks, first byte in block is id (01==text)
+ ///
+ public class Chk : SubtitleFormat
+ {
+ private readonly Encoding _codePage = Encoding.GetEncoding(850);
+ // private string _languageId = "DEN"; // English
+
+ public override string Extension => ".chk";
+
+ public const string NameOfFormat = "CHK";
+
+ public override string Name => NameOfFormat;
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (fileName.EndsWith(".chk", StringComparison.OrdinalIgnoreCase))
+ {
+ var buffer = FileUtil.ReadAllBytesShared(fileName);
+ return buffer.Length > 0 && buffer[0] == 0x1d;
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not implemented!";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ var buffer = FileUtil.ReadAllBytesShared(fileName);
+ int index = 256;
+ _errorCount = 0;
+ subtitle.Paragraphs.Clear();
+ while (index < buffer.Length)
+ {
+ Paragraph p = ReadParagraph(buffer, index);
+ if (p != null)
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+
+ index += 128;
+ }
+
+ if (subtitle.Paragraphs.Count > 1)
+ {
+ if (string.IsNullOrWhiteSpace(subtitle.Paragraphs[0].Text))
+ {
+ subtitle.Paragraphs.RemoveAt(0);
+ }
+
+ if (string.IsNullOrWhiteSpace(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].Text))
+ {
+ subtitle.Paragraphs.RemoveAt(subtitle.Paragraphs.Count - 1);
+ }
+ }
+
+ subtitle.Renumber();
+ }
+
+ private Queue _timeCodeQueue = new Queue();
+
+ private Paragraph ReadParagraph(byte[] buffer, int index)
+ {
+ if (buffer[index] == 1 && _timeCodeQueue.Count > 0) // text
+ {
+ var sb = new StringBuilder();
+ int skipCount = 0;
+ int textLength = buffer[index + 2] - 11;
+ int start = index + 13;
+
+ for (int i = 0; i <= textLength; i++)
+ {
+ if (skipCount > 0)
+ {
+ skipCount--;
+ }
+ else if (buffer[index + 13 + i] == 0xFE)
+ {
+ sb.Append(GetText(buffer, start, index + i + 13));
+ start = index + 13 + i + 3;
+ skipCount = 2;
+ sb.AppendLine();
+ }
+ else if (buffer[index + 13 + i] == 0)
+ {
+ sb.Append(GetText(buffer, start, index + i + 13));
+ break;
+ }
+ if (i == textLength)
+ {
+ sb.Append(GetText(buffer, start, index + i + 13 + 1));
+ }
+ }
+
+ Paragraph p;
+ if (_timeCodeQueue.Count > 0)
+ {
+ p = _timeCodeQueue.Dequeue();
+ }
+ else
+ {
+ p = new Paragraph();
+ }
+
+ p.Number = buffer[index + 3] * 256 + buffer[index + 4]; // Subtitle number
+ p.Text = sb.ToString();
+ //if (p.Number == 0 && p.Text.StartsWith("LANG:", StringComparison.Ordinal) && p.Text.Length > 8)
+ //{
+ // _languageId = p.Text.Substring(5, 3);
+ //}
+ return p;
+ }
+
+ if (buffer[index] == 0x0a && _timeCodeQueue.Count > 0)
+ {
+ // ?
+ }
+ else if (buffer[index] == 0x09 && _timeCodeQueue.Count > 0)
+ {
+ // ?
+ }
+ else // time codes
+ {
+ var newTimeCodes = new List();
+ for (int i = 0; i < 15; i++)
+ {
+ int start = index + 2 + (i * 8);
+ int totalFrameNumber = (buffer[start + 3] << 16) + (buffer[start + 5] << 8) + buffer[start + 4];
+ int durationInFrames = buffer[start + 6];
+ var p = new Paragraph(string.Empty, FramesToMilliseconds(totalFrameNumber), FramesToMilliseconds(totalFrameNumber + durationInFrames));
+ newTimeCodes.Add(p);
+ }
+
+ if (_timeCodeQueue.Count != 15 || IsSequential(newTimeCodes))
+ {
+ _timeCodeQueue = new Queue(newTimeCodes);
+ }
+ }
+ return null;
+ }
+
+ private static bool IsSequential(List newTimeCodes)
+ {
+ var lastMs = 0.0;
+ foreach (var p in newTimeCodes)
+ {
+ if (p.StartTime.TotalMilliseconds < lastMs)
+ {
+ return false;
+ }
+ lastMs = p.StartTime.TotalMilliseconds;
+ }
+ return true;
+ }
+
+ private string GetText(byte[] buffer, int start, int end)
+ {
+ string text = string.Empty;
+ if (buffer[start] == 0x1f && buffer[start + 1] == 0x57 && buffer[start + 2] == 0x31 && buffer[start + 3] == 0x36) // W16
+ {
+ if (end - start > 4)
+ {
+ text = Encoding.GetEncoding(950).GetString(buffer, start + 4, end - start - 4);
+ }
+ }
+ else
+ {
+ if (end - start > 0)
+ {
+ text = _codePage.GetString(buffer, start, end - start);
+ }
+ }
+ if (text.Length > 4 && text[0] == 0x1f && text[1] == 'R' && text[4] == '.' && CharUtils.IsDigit(text[2]) && CharUtils.IsDigit(text[3]))
+ {
+ text = text.Remove(0, 5);
+ }
+
+ // special language codes...
+ text = text.Replace("ÔA", "Á");
+ text = text.Replace("ÔE", "É");
+ text = text.Replace("ÔI", "Í");
+ text = text.Replace("ÓN", "Ñ");
+ text = text.Replace("ÔO", "Ó");
+ text = text.Replace("ÔU", "Ú");
+ text = text.Replace("Ôa", "á");
+ text = text.Replace("Ôe", "é");
+ text = text.Replace("Ôi", "í");
+ text = text.Replace("Ón", "ñ");
+ text = text.Replace("Ôo", "ó");
+ text = text.Replace("Ôu", "ú");
+
+ text = text.Replace("ÒA", "À");
+ text = text.Replace("ÒE", "È");
+ text = text.Replace("ÒU", "Ù");
+ text = text.Replace("Òa", "à");
+ text = text.Replace("Òe", "è");
+ text = text.Replace("Òu", "ù");
+
+ text = text.Replace("ÕU", "Ü");
+ text = text.Replace("ÕA", "Ä");
+ text = text.Replace("ÕO", "Ö");
+ text = text.Replace("Õu", "ü");
+ text = text.Replace("Õa", "ä");
+ text = text.Replace("Õo", "ö");
+
+ text = text.Replace("õa", "â");
+ text = text.Replace("õe", "ê");
+ text = text.Replace("õi", "î");
+ text = text.Replace("õu", "û");
+ text = text.Replace("õA", "Â");
+ text = text.Replace("õE", "Ê");
+ text = text.Replace("õI", "Î");
+ text = text.Replace("õU", "Û");
+
+ return ApplyFont(text);
+ }
+
+ private static string ApplyFont(string text)
+ {
+ var sb = new StringBuilder();
+ string post = string.Empty;
+ int i = 0;
+ while (i < text.Length)
+ {
+ if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 0x1D && text[i + 2] == 07)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ i += 2;
+ }
+ else if (text[i] == 01 && i < text.Length - 4 && text[i + 1] == 06)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ i++;
+ }
+ else if (text[i] == 2)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ }
+ else if (text[i] == 3)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ }
+ else if (text[i] == 6)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ }
+ else if (text[i] == 7)
+ {
+ if (post != string.Empty)
+ {
+ sb.Append("");
+ }
+
+ sb.Append("");
+ post = "";
+ }
+ else
+ {
+ sb.Append(text[i]);
+ }
+ i++;
+ }
+
+ text = sb + post;
+ if (string.IsNullOrWhiteSpace(HtmlUtil.RemoveHtmlTags(text)))
+ {
+ return string.Empty;
+ }
+
+ return text;
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/Cmaft.cs b/src/libse/SubtitleFormats/Cmaft.cs
similarity index 100%
rename from libse/SubtitleFormats/Cmaft.cs
rename to src/libse/SubtitleFormats/Cmaft.cs
diff --git a/libse/SubtitleFormats/Csv.cs b/src/libse/SubtitleFormats/Csv.cs
similarity index 97%
rename from libse/SubtitleFormats/Csv.cs
rename to src/libse/SubtitleFormats/Csv.cs
index 7c4c747d5..4172c32cf 100644
--- a/libse/SubtitleFormats/Csv.cs
+++ b/src/libse/SubtitleFormats/Csv.cs
@@ -1,96 +1,96 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Csv : SubtitleFormat
- {
- private const string Separator = ";";
- private static readonly Regex CsvLine = new Regex(@"^""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?[^""]*""?$", RegexOptions.Compiled);
-
- public override string Extension => ".csv";
-
- public override string Name => "Csv";
-
- public override bool IsMine(List lines, string fileName)
- {
- int fine = 0;
- int failed = 0;
- foreach (string line in lines)
- {
- if (CsvLine.IsMatch(line))
- {
- fine++;
- }
- else
- {
- failed++;
- }
- }
- return fine > failed;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string format = "{1}{0}{2}{0}{3}{0}\"{4}\"";
- var sb = new StringBuilder();
- sb.AppendLine(string.Format(format, Separator, "Number", "Start time in milliseconds", "End time in milliseconds", "Text"));
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendLine(string.Format(format, Separator, p.Number, p.StartTime.TotalMilliseconds, p.EndTime.TotalMilliseconds, p.Text.Replace(Environment.NewLine, "\n")));
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- bool continuation = false;
- Paragraph p = null;
- foreach (string line in lines)
- {
- if (CsvLine.IsMatch(line))
- {
- string[] parts = line.Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 4)
- {
- try
- {
- int start = Convert.ToInt32(Utilities.FixQuotes(parts[1]));
- int end = Convert.ToInt32(Utilities.FixQuotes(parts[2]));
- string text = Utilities.FixQuotes(parts[3]);
- p = new Paragraph(text, start, end);
- subtitle.Paragraphs.Add(p);
- continuation = parts[3].StartsWith('"') && !parts[3].EndsWith('"');
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- else
- {
- if (continuation)
- {
- if (p.Text.Length < 300)
- {
- p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim();
- }
-
- continuation = !line.TrimEnd().EndsWith('"');
- }
- else
- {
- _errorCount++;
- }
- }
- }
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Csv : SubtitleFormat
+ {
+ private const string Separator = ";";
+ private static readonly Regex CsvLine = new Regex(@"^""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?\d+""?" + Separator + @"""?[^""]*""?$", RegexOptions.Compiled);
+
+ public override string Extension => ".csv";
+
+ public override string Name => "Csv";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ int fine = 0;
+ int failed = 0;
+ foreach (string line in lines)
+ {
+ if (CsvLine.IsMatch(line))
+ {
+ fine++;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+ return fine > failed;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string format = "{1}{0}{2}{0}{3}{0}\"{4}\"";
+ var sb = new StringBuilder();
+ sb.AppendLine(string.Format(format, Separator, "Number", "Start time in milliseconds", "End time in milliseconds", "Text"));
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendLine(string.Format(format, Separator, p.Number, p.StartTime.TotalMilliseconds, p.EndTime.TotalMilliseconds, p.Text.Replace(Environment.NewLine, "\n")));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ bool continuation = false;
+ Paragraph p = null;
+ foreach (string line in lines)
+ {
+ if (CsvLine.IsMatch(line))
+ {
+ string[] parts = line.Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 4)
+ {
+ try
+ {
+ int start = Convert.ToInt32(Utilities.FixQuotes(parts[1]));
+ int end = Convert.ToInt32(Utilities.FixQuotes(parts[2]));
+ string text = Utilities.FixQuotes(parts[3]);
+ p = new Paragraph(text, start, end);
+ subtitle.Paragraphs.Add(p);
+ continuation = parts[3].StartsWith('"') && !parts[3].EndsWith('"');
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ else
+ {
+ if (continuation)
+ {
+ if (p.Text.Length < 300)
+ {
+ p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim();
+ }
+
+ continuation = !line.TrimEnd().EndsWith('"');
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Csv2.cs b/src/libse/SubtitleFormats/Csv2.cs
similarity index 97%
rename from libse/SubtitleFormats/Csv2.cs
rename to src/libse/SubtitleFormats/Csv2.cs
index 8e4fde18a..1c5a41e02 100644
--- a/libse/SubtitleFormats/Csv2.cs
+++ b/src/libse/SubtitleFormats/Csv2.cs
@@ -1,129 +1,129 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Csv2 : SubtitleFormat
- {
- private const string Separator = ",";
-
- //1,01:00:10:03,01:00:15:25,I thought I should let my sister-in-law know.
- private static readonly Regex CsvLine = new Regex(@"^\d+" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled);
-
- public override string Extension => ".csv";
-
- public override string Name => "Csv2";
-
- public override bool IsMine(List lines, string fileName)
- {
- int fine = 0;
- int failed = 0;
- bool continuation = false;
- foreach (string line in lines)
- {
- Match m = CsvLine.Match(line);
- if (m.Success)
- {
- fine++;
- string s = line.Remove(0, m.Length);
- continuation = s.StartsWith('"');
- }
- else if (!string.IsNullOrWhiteSpace(line))
- {
- if (continuation)
- {
- continuation = false;
- }
- else
- {
- failed++;
- }
- }
- }
- return fine > failed;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string format = "{1}{0}{2}{0}{3}{0}\"{4}\"";
- var sb = new StringBuilder();
- sb.AppendLine(string.Format(format, Separator, "Number", "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Text"));
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendLine(string.Format(format, Separator, p.Number, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\n")));
- }
- return sb.ToString().Trim();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- bool continuation = false;
- Paragraph p = null;
- foreach (string line in lines)
- {
- Match m = CsvLine.Match(line);
- if (m.Success)
- {
- string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 3)
- {
- try
- {
- var start = DecodeTimeCode(parts[1]);
- var end = DecodeTimeCode(parts[2]);
- string text = line.Remove(0, m.Length);
- continuation = text.StartsWith('"') && !text.EndsWith('"');
- text = text.Trim('"');
- p = new Paragraph(start, end, text);
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- else if (!string.IsNullOrWhiteSpace(line))
- {
- if (continuation)
- {
- if (p != null && p.Text.Length < 300)
- {
- p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim();
- }
-
- continuation = !line.TrimEnd().EndsWith('"');
- }
- else
- {
- _errorCount++;
- }
- }
- }
- subtitle.Renumber();
- }
-
- private static TimeCode DecodeTimeCode(string part)
- {
- string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries);
-
- //00:00:07:12
- string hour = parts[0];
- string minutes = parts[1];
- string seconds = parts[2];
- string frames = parts[3];
-
- return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames)));
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Csv2 : SubtitleFormat
+ {
+ private const string Separator = ",";
+
+ //1,01:00:10:03,01:00:15:25,I thought I should let my sister-in-law know.
+ private static readonly Regex CsvLine = new Regex(@"^\d+" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled);
+
+ public override string Extension => ".csv";
+
+ public override string Name => "Csv2";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ int fine = 0;
+ int failed = 0;
+ bool continuation = false;
+ foreach (string line in lines)
+ {
+ Match m = CsvLine.Match(line);
+ if (m.Success)
+ {
+ fine++;
+ string s = line.Remove(0, m.Length);
+ continuation = s.StartsWith('"');
+ }
+ else if (!string.IsNullOrWhiteSpace(line))
+ {
+ if (continuation)
+ {
+ continuation = false;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+ }
+ return fine > failed;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string format = "{1}{0}{2}{0}{3}{0}\"{4}\"";
+ var sb = new StringBuilder();
+ sb.AppendLine(string.Format(format, Separator, "Number", "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Text"));
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendLine(string.Format(format, Separator, p.Number, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\n")));
+ }
+ return sb.ToString().Trim();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ bool continuation = false;
+ Paragraph p = null;
+ foreach (string line in lines)
+ {
+ Match m = CsvLine.Match(line);
+ if (m.Success)
+ {
+ string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 3)
+ {
+ try
+ {
+ var start = DecodeTimeCode(parts[1]);
+ var end = DecodeTimeCode(parts[2]);
+ string text = line.Remove(0, m.Length);
+ continuation = text.StartsWith('"') && !text.EndsWith('"');
+ text = text.Trim('"');
+ p = new Paragraph(start, end, text);
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ else if (!string.IsNullOrWhiteSpace(line))
+ {
+ if (continuation)
+ {
+ if (p != null && p.Text.Length < 300)
+ {
+ p.Text = (p.Text + Environment.NewLine + line.TrimEnd('"')).Trim();
+ }
+
+ continuation = !line.TrimEnd().EndsWith('"');
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static TimeCode DecodeTimeCode(string part)
+ {
+ string[] parts = part.Split(new[] { '.', ':' }, StringSplitOptions.RemoveEmptyEntries);
+
+ //00:00:07:12
+ string hour = parts[0];
+ string minutes = parts[1];
+ string seconds = parts[2];
+ string frames = parts[3];
+
+ return new TimeCode(int.Parse(hour), int.Parse(minutes), int.Parse(seconds), FramesToMillisecondsMax999(int.Parse(frames)));
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Csv3.cs b/src/libse/SubtitleFormats/Csv3.cs
similarity index 97%
rename from libse/SubtitleFormats/Csv3.cs
rename to src/libse/SubtitleFormats/Csv3.cs
index 643e01d20..c538809b1 100644
--- a/libse/SubtitleFormats/Csv3.cs
+++ b/src/libse/SubtitleFormats/Csv3.cs
@@ -1,195 +1,195 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Csv3 : SubtitleFormat
- {
- private const string Separator = ",";
-
- //01:00:10:03,01:00:15:25,"I thought I should let my sister-in-law know.", ""
- private static readonly Regex CsvLine = new Regex(@"^\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled);
-
- public override string Extension => ".csv";
-
- public override string Name => "Csv3";
-
- public override bool IsMine(List lines, string fileName)
- {
- int fine = 0;
- int failed = 0;
- bool continuation = false;
- foreach (string line in lines)
- {
- if (line.StartsWith("$FontName", StringComparison.Ordinal) || line.StartsWith("$ColorIndex1", StringComparison.Ordinal))
- {
- return false;
- }
-
- Match m = null;
- if (line.Length > 8 && line[2] == ':')
- {
- m = CsvLine.Match(line);
- }
-
- if (m != null && m.Success)
- {
- fine++;
- string s = line.Remove(0, m.Length);
- continuation = s.StartsWith('"');
- }
- else if (!string.IsNullOrWhiteSpace(line))
- {
- if (continuation)
- {
- continuation = false;
- }
- else
- {
- failed++;
- }
- }
- }
- if (failed > 20)
- {
- return false;
- }
-
- return fine > failed;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string format = "{1}{0}{2}{0}\"{3}\"{0}\"{4}\"";
- var sb = new StringBuilder();
- sb.AppendLine(string.Format(format, Separator, "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Line 1", "Line 2"));
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- var arr = p.Text.Trim().SplitToLines();
- if (arr.Count > 3)
- {
- string s = Utilities.AutoBreakLine(p.Text);
- arr = s.Trim().SplitToLines();
- }
- string line1 = string.Empty;
- string line2 = string.Empty;
- line1 = arr[0];
- if (arr.Count > 1)
- {
- line2 = arr[1];
- }
-
- line1 = line1.Replace("\"", "\"\"");
- line2 = line2.Replace("\"", "\"\"");
- sb.AppendLine(string.Format(format, Separator, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), line1, line2));
- }
- return sb.ToString().Trim();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- char[] splitChars = { '.', ':' };
- foreach (string line in lines)
- {
- Match m = CsvLine.Match(line);
- if (m.Success)
- {
- string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 2)
- {
- try
- {
- var start = DecodeTimeCodeFrames(parts[0], splitChars);
- var end = DecodeTimeCodeFrames(parts[1], splitChars);
- string text = ReadText(line.Remove(0, m.Length));
- var p = new Paragraph(start, end, text);
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- else if (!string.IsNullOrWhiteSpace(line))
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
-
- private static string ReadText(string csv)
- {
- if (string.IsNullOrEmpty(csv))
- {
- return string.Empty;
- }
-
- csv = csv.Replace("\"\"", "\"");
-
- var sb = new StringBuilder();
- csv = csv.Trim();
- if (csv.StartsWith('"'))
- {
- csv = csv.Remove(0, 1);
- }
-
- if (csv.EndsWith('"'))
- {
- csv = csv.Remove(csv.Length - 1, 1);
- }
-
- bool isBreak = false;
- for (int i = 0; i < csv.Length; i++)
- {
- var s = csv[i];
- if (s == '"' && csv.Substring(i).StartsWith("\"\"", StringComparison.Ordinal))
- {
- sb.Append('"');
- }
- else if (s == '"')
- {
- if (isBreak)
- {
- isBreak = false;
- }
- else if (i == 0 || i == csv.Length - 1 || sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
- {
- sb.Append('"');
- }
- else
- {
- isBreak = true;
- }
- }
- else
- {
- if (isBreak && s == ' ')
- {
- }
- else if (isBreak && s == ',')
- {
- sb.Append(Environment.NewLine);
- }
- else
- {
- isBreak = false;
- sb.Append(s);
- }
- }
- }
- return sb.ToString().Trim();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Csv3 : SubtitleFormat
+ {
+ private const string Separator = ",";
+
+ //01:00:10:03,01:00:15:25,"I thought I should let my sister-in-law know.", ""
+ private static readonly Regex CsvLine = new Regex(@"^\d\d:\d\d:\d\d:\d\d" + Separator + @"\d\d:\d\d:\d\d:\d\d" + Separator, RegexOptions.Compiled);
+
+ public override string Extension => ".csv";
+
+ public override string Name => "Csv3";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ int fine = 0;
+ int failed = 0;
+ bool continuation = false;
+ foreach (string line in lines)
+ {
+ if (line.StartsWith("$FontName", StringComparison.Ordinal) || line.StartsWith("$ColorIndex1", StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ Match m = null;
+ if (line.Length > 8 && line[2] == ':')
+ {
+ m = CsvLine.Match(line);
+ }
+
+ if (m != null && m.Success)
+ {
+ fine++;
+ string s = line.Remove(0, m.Length);
+ continuation = s.StartsWith('"');
+ }
+ else if (!string.IsNullOrWhiteSpace(line))
+ {
+ if (continuation)
+ {
+ continuation = false;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+ }
+ if (failed > 20)
+ {
+ return false;
+ }
+
+ return fine > failed;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string format = "{1}{0}{2}{0}\"{3}\"{0}\"{4}\"";
+ var sb = new StringBuilder();
+ sb.AppendLine(string.Format(format, Separator, "Start time (hh:mm:ss:ff)", "End time (hh:mm:ss:ff)", "Line 1", "Line 2"));
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ var arr = p.Text.Trim().SplitToLines();
+ if (arr.Count > 3)
+ {
+ string s = Utilities.AutoBreakLine(p.Text);
+ arr = s.Trim().SplitToLines();
+ }
+ string line1 = string.Empty;
+ string line2 = string.Empty;
+ line1 = arr[0];
+ if (arr.Count > 1)
+ {
+ line2 = arr[1];
+ }
+
+ line1 = line1.Replace("\"", "\"\"");
+ line2 = line2.Replace("\"", "\"\"");
+ sb.AppendLine(string.Format(format, Separator, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), line1, line2));
+ }
+ return sb.ToString().Trim();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ char[] splitChars = { '.', ':' };
+ foreach (string line in lines)
+ {
+ Match m = CsvLine.Match(line);
+ if (m.Success)
+ {
+ string[] parts = line.Substring(0, m.Length).Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2)
+ {
+ try
+ {
+ var start = DecodeTimeCodeFrames(parts[0], splitChars);
+ var end = DecodeTimeCodeFrames(parts[1], splitChars);
+ string text = ReadText(line.Remove(0, m.Length));
+ var p = new Paragraph(start, end, text);
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ else if (!string.IsNullOrWhiteSpace(line))
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static string ReadText(string csv)
+ {
+ if (string.IsNullOrEmpty(csv))
+ {
+ return string.Empty;
+ }
+
+ csv = csv.Replace("\"\"", "\"");
+
+ var sb = new StringBuilder();
+ csv = csv.Trim();
+ if (csv.StartsWith('"'))
+ {
+ csv = csv.Remove(0, 1);
+ }
+
+ if (csv.EndsWith('"'))
+ {
+ csv = csv.Remove(csv.Length - 1, 1);
+ }
+
+ bool isBreak = false;
+ for (int i = 0; i < csv.Length; i++)
+ {
+ var s = csv[i];
+ if (s == '"' && csv.Substring(i).StartsWith("\"\"", StringComparison.Ordinal))
+ {
+ sb.Append('"');
+ }
+ else if (s == '"')
+ {
+ if (isBreak)
+ {
+ isBreak = false;
+ }
+ else if (i == 0 || i == csv.Length - 1 || sb.ToString().EndsWith(Environment.NewLine, StringComparison.Ordinal))
+ {
+ sb.Append('"');
+ }
+ else
+ {
+ isBreak = true;
+ }
+ }
+ else
+ {
+ if (isBreak && s == ' ')
+ {
+ }
+ else if (isBreak && s == ',')
+ {
+ sb.Append(Environment.NewLine);
+ }
+ else
+ {
+ isBreak = false;
+ sb.Append(s);
+ }
+ }
+ }
+ return sb.ToString().Trim();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Csv4.cs b/src/libse/SubtitleFormats/Csv4.cs
similarity index 100%
rename from libse/SubtitleFormats/Csv4.cs
rename to src/libse/SubtitleFormats/Csv4.cs
diff --git a/libse/SubtitleFormats/Csv5.cs b/src/libse/SubtitleFormats/Csv5.cs
similarity index 100%
rename from libse/SubtitleFormats/Csv5.cs
rename to src/libse/SubtitleFormats/Csv5.cs
diff --git a/libse/SubtitleFormats/CsvNuendo.cs b/src/libse/SubtitleFormats/CsvNuendo.cs
similarity index 100%
rename from libse/SubtitleFormats/CsvNuendo.cs
rename to src/libse/SubtitleFormats/CsvNuendo.cs
diff --git a/libse/SubtitleFormats/DCinemaInterop.cs b/src/libse/SubtitleFormats/DCinemaInterop.cs
similarity index 100%
rename from libse/SubtitleFormats/DCinemaInterop.cs
rename to src/libse/SubtitleFormats/DCinemaInterop.cs
diff --git a/libse/SubtitleFormats/DCinemaSmpte2007.cs b/src/libse/SubtitleFormats/DCinemaSmpte2007.cs
similarity index 97%
rename from libse/SubtitleFormats/DCinemaSmpte2007.cs
rename to src/libse/SubtitleFormats/DCinemaSmpte2007.cs
index f085621ac..31f4940ef 100644
--- a/libse/SubtitleFormats/DCinemaSmpte2007.cs
+++ b/src/libse/SubtitleFormats/DCinemaSmpte2007.cs
@@ -1,841 +1,841 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Text;
-using System.Xml;
-using System.Xml.Schema;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DCinemaSmpte2007 : SubtitleFormat
- {
- //
- //
- // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e
- // 2001, A Space Odissey
- // This is a subtitle file
- // 2012-06-26T12:33:59.000-00:00
- // 1
- // fr
- // 25 1
- // 25
- // 00:00:00:00
- // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391
- //
- //
- // Hallo
- //
- //
-
- public string Errors { get; private set; }
-
- private double _frameRate = 24;
-
- public int Version { get; set; }
-
- public override string Extension => ".xml";
-
- public override string Name => "D-Cinema SMPTE 2007";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string xmlAsString = sb.ToString().Trim();
- if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2010/DCST") ||
- xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2014/DCST"))
- {
- return false;
- }
-
- if (xmlAsString.Contains(" 0;
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- return false;
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- Errors = null;
- var ss = Configuration.Settings.SubtitleSettings;
-
- if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate))
- {
- string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- double d1, d2;
- if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2))
- {
- _frameRate = d1 / d2;
- }
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine +
- " " + Environment.NewLine +
- " This is a subtitle file" + Environment.NewLine +
- " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine +
- " 1" + Environment.NewLine +
- " en" + Environment.NewLine +
- " 25 1" + Environment.NewLine +
- " 25" + Environment.NewLine +
- " 00:00:00:00 " + Environment.NewLine +
- " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.PreserveWhitespace = true;
- var nsmgr = new XmlNamespaceManager(xml.NameTable);
- nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI);
-
- if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle))
- {
- ss.CurrentDCinemaMovieTitle = title;
- }
-
- if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect))
- {
- Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true);
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:"))
- {
- ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid();
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId;
- xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber;
- xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage))
- {
- ss.CurrentDCinemaLanguage = "en";
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage;
- if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null)
- {
- if (Configuration.Settings.General.CurrentFrameRate == 24)
- {
- ss.CurrentDCinemaEditRate = "24 1";
- ss.CurrentDCinemaTimeCodeRate = "24";
- }
- else
- {
- ss.CurrentDCinemaEditRate = "25 1";
- ss.CurrentDCinemaTimeCodeRate = "25";
- }
- }
- xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate;
- xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime))
- {
- ss.CurrentDCinemaStartTime = "00:00:00:00";
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime;
- xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri;
- int fontSize = ss.CurrentDCinemaFontSize;
- string loadedFontId = "Font1";
- if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId))
- {
- loadedFontId = ss.CurrentDCinemaFontId;
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString();
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpperInvariant();
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpperInvariant();
-
- XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr);
- int no = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- if (p.Text != null)
- {
- XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst");
-
- XmlAttribute id = xml.CreateAttribute("SpotNumber");
- id.InnerText = (no + 1).ToString();
- subNode.Attributes.Append(id);
-
- XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime");
- fadeUpTime.InnerText = "00:00:00:00";
- subNode.Attributes.Append(fadeUpTime);
-
- XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime");
- fadeDownTime.InnerText = "00:00:00:00";
- subNode.Attributes.Append(fadeDownTime);
-
- XmlAttribute start = xml.CreateAttribute("TimeIn");
- start.InnerText = ConvertToTimeString(p.StartTime);
- subNode.Attributes.Append(start);
-
- XmlAttribute end = xml.CreateAttribute("TimeOut");
- end.InnerText = ConvertToTimeString(p.EndTime);
- subNode.Attributes.Append(end);
-
- bool alignLeft = p.Text.StartsWith("{\\a1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignRight = p.Text.StartsWith("{\\a3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignVTop = p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignVCenter = p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a10}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal); // advanced sub station alpha
-
- string text = Utilities.RemoveSsaTags(p.Text);
-
- var lines = text.SplitToLines();
- int vPos = 1 + lines.Count * 7;
- int vPosFactor = (int)Math.Round(fontSize / 7.4);
- if (alignVTop)
- {
- vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
- }
- else if (alignVCenter)
- {
- vPos = (int)Math.Round((lines.Count * vPosFactor * -1) / 2.0);
- }
- else
- {
- vPos = lines.Count * vPosFactor - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
- }
-
- bool isItalic = false;
- int fontNo = 0;
- Stack fontColors = new Stack();
- foreach (string line in lines)
- {
- XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
-
- XmlAttribute vPosition = xml.CreateAttribute("Vposition");
- vPosition.InnerText = vPos.ToString();
- textNode.Attributes.Append(vPosition);
-
- XmlAttribute vAlign = xml.CreateAttribute("Valign");
- if (alignVTop)
- {
- vAlign.InnerText = "top";
- }
- else if (alignVCenter)
- {
- vAlign.InnerText = "center";
- }
- else
- {
- vAlign.InnerText = "bottom";
- }
-
- textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign);
-
- XmlAttribute hAlign = xml.CreateAttribute("Halign");
- if (alignLeft)
- {
- hAlign.InnerText = "left";
- }
- else if (alignRight)
- {
- hAlign.InnerText = "right";
- }
- else
- {
- hAlign.InnerText = "center";
- }
-
- textNode.Attributes.Append(hAlign);
-
- XmlAttribute direction = xml.CreateAttribute("Direction");
- direction.InnerText = "ltr";
- textNode.Attributes.Append(direction);
-
- int i = 0;
- var txt = new StringBuilder();
- var html = new StringBuilder();
- XmlNode nodeTemp = xml.CreateElement("temp");
- while (i < line.Length)
- {
- if (!isItalic && line.Substring(i).StartsWith(""))
- {
- if (txt.Length > 0)
- {
- nodeTemp.InnerText = txt.ToString();
- html.Append(nodeTemp.InnerXml);
- txt.Clear();
- }
- isItalic = true;
- i += 2;
- }
- else if (isItalic && line.Substring(i).StartsWith(""))
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
-
- if (line.Length > i + 5 && line.Substring(i + 4).StartsWith(""))
- {
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Pop();
- fontNode.Attributes.Append(fontColor);
- fontNo--;
- i += 7;
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- txt.Clear();
- }
- isItalic = false;
- i += 3;
- }
- else if (line.Substring(i).StartsWith(""))
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Pop();
- fontNode.Attributes.Append(fontColor);
-
- if (line.Length > i + 9 && line.Substring(i + 7).StartsWith(""))
- {
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
- isItalic = false;
- i += 4;
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- txt.Clear();
- }
- fontNo--;
- i += 6;
- }
- else
- {
- txt.Append(line[i]);
- }
- i++;
- }
-
- if (fontNo > 0)
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Peek();
- fontNode.Attributes.Append(fontColor);
-
- if (isItalic)
- {
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- }
- else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + "");
- XmlNode fontNode = xml.CreateElement("dcst:Font");
- fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml;
- foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes)
- {
- XmlAttribute newA = xml.CreateAttribute(a.Name);
- newA.InnerText = a.InnerText;
- fontNode.Attributes.Append(newA);
- }
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Peek();
- fontNode.Attributes.Append(fontColor);
-
- html.Clear();
- html.Append(fontNode.OuterXml);
- }
- }
- else if (isItalic)
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line);
- html.Append(fontNode.OuterXml);
- }
- }
- else
- {
- if (txt.Length > 0)
- {
- nodeTemp.InnerText = txt.ToString();
- html.Append(nodeTemp.InnerXml);
- }
- }
- textNode.InnerXml = html.ToString();
-
- subNode.AppendChild(textNode);
- if (alignVTop)
- {
- vPos += vPosFactor;
- }
- else
- {
- vPos -= vPosFactor;
- }
- }
- if (subNode.InnerXml.Length == 0)
- { // Empty text is just one space
- XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
- textNode.InnerXml = " ";
- subNode.AppendChild(textNode);
-
- XmlAttribute vPosition = xml.CreateAttribute("Vposition");
- vPosition.InnerText = vPos.ToString();
- textNode.Attributes.Append(vPosition);
-
- XmlAttribute vAlign = xml.CreateAttribute("Valign");
- vAlign.InnerText = "bottom";
- textNode.Attributes.Append(vAlign);
- }
- mainListFont.AppendChild(subNode);
- no++;
- }
- }
- string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty);
-
- const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2007-DCST.xsd.gz";
- System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
- Stream strm = asm.GetManifestResourceStream(res);
- if (strm != null)
- {
- try
- {
- var xmld = new XmlDocument();
- var rdr = new StreamReader(strm);
- var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress);
- xmld.LoadXml(result);
- using (var xr = XmlReader.Create(zip))
- {
- xmld.Schemas.Add(null, xr);
- xmld.Validate(ValidationCallBack);
- }
- }
- catch (Exception exception)
- {
- Errors = "Error validating xml via SMPTE - 428 - 7 - 2007 - DCST.xsd: " + exception.Message;
- }
- }
- return DCinemaSmpte2010.FixDcsTextSameLine(result);
- }
-
- private void ValidationCallBack(object sender, ValidationEventArgs e)
- {
- throw new Exception(e.Message);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0)
- {
- pText.AppendLine();
- }
-
- lastVPosition = vPosition;
- }
- }
-
- bool alignLeft = false;
- bool alignRight = false;
- bool alignVTop = false;
- bool alignVCenter = false;
- if (innerNode.Attributes["Halign"] != null)
- {
- string hAlign = innerNode.Attributes["Halign"].InnerText;
- if (hAlign == "left")
- {
- alignLeft = true;
- }
- else if (hAlign == "right")
- {
- alignRight = true;
- }
- }
-
- if (innerNode.Attributes["Valign"] != null)
- {
- string hAlign = innerNode.Attributes["Valign"].InnerText;
- if (hAlign == "top")
- {
- alignVTop = true;
- }
- else if (hAlign == "center")
- {
- alignVCenter = true;
- }
- }
-
- if (alignLeft || alignRight || alignVCenter || alignVTop)
- {
- if (!pText.ToString().StartsWith("{\\an"))
- {
- string pre = string.Empty;
- if (alignVTop)
- {
- if (alignLeft)
- {
- pre = "{\\an7}";
- }
- else if (alignRight)
- {
- pre = "{\\an9}";
- }
- else
- {
- pre = "{\\an8}";
- }
- }
- else if (alignVCenter)
- {
- if (alignLeft)
- {
- pre = "{\\an4}";
- }
- else if (alignRight)
- {
- pre = "{\\an6}";
- }
- else
- {
- pre = "{\\an5}";
- }
- }
- else
- {
- if (alignLeft)
- {
- pre = "{\\an1}";
- }
- else if (alignRight)
- {
- pre = "{\\an3}";
- }
- }
-
- string temp = pre + pText;
- pText.Clear();
- pText.Append(temp);
- }
- }
-
- if (innerNode.ChildNodes.Count == 0)
- {
- pText.Append(innerNode.InnerText);
- }
- else
- {
- foreach (XmlNode innerInnerNode in innerNode)
- {
- if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null &&
- innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
- {
- if (innerInnerNode.Attributes["Color"] != null)
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- else
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- }
- else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null)
- {
- if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- else
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- }
- else
- {
- pText.Append(innerInnerNode.InnerText);
- }
- }
- }
- }
- else
- {
- pText.Append(innerNode.InnerText);
- }
- }
- string start = node.Attributes["TimeIn"].InnerText;
- string end = node.Attributes["TimeOut"].InnerText;
-
- if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) &&
- !pText.ToString().Contains(""))
- {
- string text = pText.ToString();
- if (text.StartsWith("{\\an") && text.Length > 6)
- {
- text = text.Insert(6, "") + "";
- }
- else
- {
- text = "" + text + "";
- }
- pText = new StringBuilder(text);
- }
-
- subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString()));
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
-
- if (subtitle.Paragraphs.Count > 0)
- {
- subtitle.Header = xml.OuterXml; // save id/language/font for later use
- }
-
- subtitle.Renumber();
- }
-
- private static string GetColorStringFromDCinema(string p)
- {
- string s = p.ToLowerInvariant().Trim();
- if (s.Replace("#", string.Empty).
- Replace("0", string.Empty).
- Replace("1", string.Empty).
- Replace("2", string.Empty).
- Replace("3", string.Empty).
- Replace("4", string.Empty).
- Replace("5", string.Empty).
- Replace("6", string.Empty).
- Replace("7", string.Empty).
- Replace("8", string.Empty).
- Replace("9", string.Empty).
- Replace("a", string.Empty).
- Replace("b", string.Empty).
- Replace("c", string.Empty).
- Replace("d", string.Empty).
- Replace("e", string.Empty).
- Replace("f", string.Empty).Length == 0)
- {
- if (s.StartsWith('#'))
- {
- return s;
- }
- return "#" + s;
- }
- return p;
- }
-
- private TimeCode GetTimeCode(string s)
- {
- var parts = s.Split(':', '.', ',');
-
- int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate));
- if (milliseconds > 999)
- {
- milliseconds = 999;
- }
-
- return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds);
- }
-
- private string ConvertToTimeString(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{DCinemaSmpte2010.MsToFramesMaxFrameRate(time.Milliseconds, _frameRate):00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+using System.Xml;
+using System.Xml.Schema;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DCinemaSmpte2007 : SubtitleFormat
+ {
+ //
+ //
+ // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e
+ // 2001, A Space Odissey
+ // This is a subtitle file
+ // 2012-06-26T12:33:59.000-00:00
+ // 1
+ // fr
+ // 25 1
+ // 25
+ // 00:00:00:00
+ // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391
+ //
+ //
+ // Hallo
+ //
+ //
+
+ public string Errors { get; private set; }
+
+ private double _frameRate = 24;
+
+ public int Version { get; set; }
+
+ public override string Extension => ".xml";
+
+ public override string Name => "D-Cinema SMPTE 2007";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string xmlAsString = sb.ToString().Trim();
+ if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2010/DCST") ||
+ xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2014/DCST"))
+ {
+ return false;
+ }
+
+ if (xmlAsString.Contains(" 0;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ Errors = null;
+ var ss = Configuration.Settings.SubtitleSettings;
+
+ if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate))
+ {
+ string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ double d1, d2;
+ if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2))
+ {
+ _frameRate = d1 / d2;
+ }
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " This is a subtitle file" + Environment.NewLine +
+ " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine +
+ " 1" + Environment.NewLine +
+ " en" + Environment.NewLine +
+ " 25 1" + Environment.NewLine +
+ " 25" + Environment.NewLine +
+ " 00:00:00:00 " + Environment.NewLine +
+ " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.PreserveWhitespace = true;
+ var nsmgr = new XmlNamespaceManager(xml.NameTable);
+ nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI);
+
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle))
+ {
+ ss.CurrentDCinemaMovieTitle = title;
+ }
+
+ if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect))
+ {
+ Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true);
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:"))
+ {
+ ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid();
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId;
+ xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber;
+ xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage))
+ {
+ ss.CurrentDCinemaLanguage = "en";
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage;
+ if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate == 24)
+ {
+ ss.CurrentDCinemaEditRate = "24 1";
+ ss.CurrentDCinemaTimeCodeRate = "24";
+ }
+ else
+ {
+ ss.CurrentDCinemaEditRate = "25 1";
+ ss.CurrentDCinemaTimeCodeRate = "25";
+ }
+ }
+ xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate;
+ xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime))
+ {
+ ss.CurrentDCinemaStartTime = "00:00:00:00";
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime;
+ xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri;
+ int fontSize = ss.CurrentDCinemaFontSize;
+ string loadedFontId = "Font1";
+ if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId))
+ {
+ loadedFontId = ss.CurrentDCinemaFontId;
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString();
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpperInvariant();
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpperInvariant();
+
+ XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr);
+ int no = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (p.Text != null)
+ {
+ XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst");
+
+ XmlAttribute id = xml.CreateAttribute("SpotNumber");
+ id.InnerText = (no + 1).ToString();
+ subNode.Attributes.Append(id);
+
+ XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime");
+ fadeUpTime.InnerText = "00:00:00:00";
+ subNode.Attributes.Append(fadeUpTime);
+
+ XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime");
+ fadeDownTime.InnerText = "00:00:00:00";
+ subNode.Attributes.Append(fadeDownTime);
+
+ XmlAttribute start = xml.CreateAttribute("TimeIn");
+ start.InnerText = ConvertToTimeString(p.StartTime);
+ subNode.Attributes.Append(start);
+
+ XmlAttribute end = xml.CreateAttribute("TimeOut");
+ end.InnerText = ConvertToTimeString(p.EndTime);
+ subNode.Attributes.Append(end);
+
+ bool alignLeft = p.Text.StartsWith("{\\a1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignRight = p.Text.StartsWith("{\\a3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignVTop = p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignVCenter = p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a10}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal); // advanced sub station alpha
+
+ string text = Utilities.RemoveSsaTags(p.Text);
+
+ var lines = text.SplitToLines();
+ int vPos = 1 + lines.Count * 7;
+ int vPosFactor = (int)Math.Round(fontSize / 7.4);
+ if (alignVTop)
+ {
+ vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
+ }
+ else if (alignVCenter)
+ {
+ vPos = (int)Math.Round((lines.Count * vPosFactor * -1) / 2.0);
+ }
+ else
+ {
+ vPos = lines.Count * vPosFactor - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
+ }
+
+ bool isItalic = false;
+ int fontNo = 0;
+ Stack fontColors = new Stack();
+ foreach (string line in lines)
+ {
+ XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
+
+ XmlAttribute vPosition = xml.CreateAttribute("Vposition");
+ vPosition.InnerText = vPos.ToString();
+ textNode.Attributes.Append(vPosition);
+
+ XmlAttribute vAlign = xml.CreateAttribute("Valign");
+ if (alignVTop)
+ {
+ vAlign.InnerText = "top";
+ }
+ else if (alignVCenter)
+ {
+ vAlign.InnerText = "center";
+ }
+ else
+ {
+ vAlign.InnerText = "bottom";
+ }
+
+ textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign);
+
+ XmlAttribute hAlign = xml.CreateAttribute("Halign");
+ if (alignLeft)
+ {
+ hAlign.InnerText = "left";
+ }
+ else if (alignRight)
+ {
+ hAlign.InnerText = "right";
+ }
+ else
+ {
+ hAlign.InnerText = "center";
+ }
+
+ textNode.Attributes.Append(hAlign);
+
+ XmlAttribute direction = xml.CreateAttribute("Direction");
+ direction.InnerText = "ltr";
+ textNode.Attributes.Append(direction);
+
+ int i = 0;
+ var txt = new StringBuilder();
+ var html = new StringBuilder();
+ XmlNode nodeTemp = xml.CreateElement("temp");
+ while (i < line.Length)
+ {
+ if (!isItalic && line.Substring(i).StartsWith(""))
+ {
+ if (txt.Length > 0)
+ {
+ nodeTemp.InnerText = txt.ToString();
+ html.Append(nodeTemp.InnerXml);
+ txt.Clear();
+ }
+ isItalic = true;
+ i += 2;
+ }
+ else if (isItalic && line.Substring(i).StartsWith(""))
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+
+ if (line.Length > i + 5 && line.Substring(i + 4).StartsWith(""))
+ {
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Pop();
+ fontNode.Attributes.Append(fontColor);
+ fontNo--;
+ i += 7;
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ txt.Clear();
+ }
+ isItalic = false;
+ i += 3;
+ }
+ else if (line.Substring(i).StartsWith(""))
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Pop();
+ fontNode.Attributes.Append(fontColor);
+
+ if (line.Length > i + 9 && line.Substring(i + 7).StartsWith(""))
+ {
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+ isItalic = false;
+ i += 4;
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ txt.Clear();
+ }
+ fontNo--;
+ i += 6;
+ }
+ else
+ {
+ txt.Append(line[i]);
+ }
+ i++;
+ }
+
+ if (fontNo > 0)
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Peek();
+ fontNode.Attributes.Append(fontColor);
+
+ if (isItalic)
+ {
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ }
+ else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + "");
+ XmlNode fontNode = xml.CreateElement("dcst:Font");
+ fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml;
+ foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes)
+ {
+ XmlAttribute newA = xml.CreateAttribute(a.Name);
+ newA.InnerText = a.InnerText;
+ fontNode.Attributes.Append(newA);
+ }
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Peek();
+ fontNode.Attributes.Append(fontColor);
+
+ html.Clear();
+ html.Append(fontNode.OuterXml);
+ }
+ }
+ else if (isItalic)
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line);
+ html.Append(fontNode.OuterXml);
+ }
+ }
+ else
+ {
+ if (txt.Length > 0)
+ {
+ nodeTemp.InnerText = txt.ToString();
+ html.Append(nodeTemp.InnerXml);
+ }
+ }
+ textNode.InnerXml = html.ToString();
+
+ subNode.AppendChild(textNode);
+ if (alignVTop)
+ {
+ vPos += vPosFactor;
+ }
+ else
+ {
+ vPos -= vPosFactor;
+ }
+ }
+ if (subNode.InnerXml.Length == 0)
+ { // Empty text is just one space
+ XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
+ textNode.InnerXml = " ";
+ subNode.AppendChild(textNode);
+
+ XmlAttribute vPosition = xml.CreateAttribute("Vposition");
+ vPosition.InnerText = vPos.ToString();
+ textNode.Attributes.Append(vPosition);
+
+ XmlAttribute vAlign = xml.CreateAttribute("Valign");
+ vAlign.InnerText = "bottom";
+ textNode.Attributes.Append(vAlign);
+ }
+ mainListFont.AppendChild(subNode);
+ no++;
+ }
+ }
+ string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty);
+
+ const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2007-DCST.xsd.gz";
+ System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
+ Stream strm = asm.GetManifestResourceStream(res);
+ if (strm != null)
+ {
+ try
+ {
+ var xmld = new XmlDocument();
+ var rdr = new StreamReader(strm);
+ var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress);
+ xmld.LoadXml(result);
+ using (var xr = XmlReader.Create(zip))
+ {
+ xmld.Schemas.Add(null, xr);
+ xmld.Validate(ValidationCallBack);
+ }
+ }
+ catch (Exception exception)
+ {
+ Errors = "Error validating xml via SMPTE - 428 - 7 - 2007 - DCST.xsd: " + exception.Message;
+ }
+ }
+ return DCinemaSmpte2010.FixDcsTextSameLine(result);
+ }
+
+ private void ValidationCallBack(object sender, ValidationEventArgs e)
+ {
+ throw new Exception(e.Message);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0)
+ {
+ pText.AppendLine();
+ }
+
+ lastVPosition = vPosition;
+ }
+ }
+
+ bool alignLeft = false;
+ bool alignRight = false;
+ bool alignVTop = false;
+ bool alignVCenter = false;
+ if (innerNode.Attributes["Halign"] != null)
+ {
+ string hAlign = innerNode.Attributes["Halign"].InnerText;
+ if (hAlign == "left")
+ {
+ alignLeft = true;
+ }
+ else if (hAlign == "right")
+ {
+ alignRight = true;
+ }
+ }
+
+ if (innerNode.Attributes["Valign"] != null)
+ {
+ string hAlign = innerNode.Attributes["Valign"].InnerText;
+ if (hAlign == "top")
+ {
+ alignVTop = true;
+ }
+ else if (hAlign == "center")
+ {
+ alignVCenter = true;
+ }
+ }
+
+ if (alignLeft || alignRight || alignVCenter || alignVTop)
+ {
+ if (!pText.ToString().StartsWith("{\\an"))
+ {
+ string pre = string.Empty;
+ if (alignVTop)
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an7}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an9}";
+ }
+ else
+ {
+ pre = "{\\an8}";
+ }
+ }
+ else if (alignVCenter)
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an4}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an6}";
+ }
+ else
+ {
+ pre = "{\\an5}";
+ }
+ }
+ else
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an1}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an3}";
+ }
+ }
+
+ string temp = pre + pText;
+ pText.Clear();
+ pText.Append(temp);
+ }
+ }
+
+ if (innerNode.ChildNodes.Count == 0)
+ {
+ pText.Append(innerNode.InnerText);
+ }
+ else
+ {
+ foreach (XmlNode innerInnerNode in innerNode)
+ {
+ if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null &&
+ innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
+ {
+ if (innerInnerNode.Attributes["Color"] != null)
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ else
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ }
+ else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null)
+ {
+ if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ else
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ }
+ else
+ {
+ pText.Append(innerInnerNode.InnerText);
+ }
+ }
+ }
+ }
+ else
+ {
+ pText.Append(innerNode.InnerText);
+ }
+ }
+ string start = node.Attributes["TimeIn"].InnerText;
+ string end = node.Attributes["TimeOut"].InnerText;
+
+ if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) &&
+ !pText.ToString().Contains(""))
+ {
+ string text = pText.ToString();
+ if (text.StartsWith("{\\an") && text.Length > 6)
+ {
+ text = text.Insert(6, "") + "";
+ }
+ else
+ {
+ text = "" + text + "";
+ }
+ pText = new StringBuilder(text);
+ }
+
+ subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString()));
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ subtitle.Header = xml.OuterXml; // save id/language/font for later use
+ }
+
+ subtitle.Renumber();
+ }
+
+ private static string GetColorStringFromDCinema(string p)
+ {
+ string s = p.ToLowerInvariant().Trim();
+ if (s.Replace("#", string.Empty).
+ Replace("0", string.Empty).
+ Replace("1", string.Empty).
+ Replace("2", string.Empty).
+ Replace("3", string.Empty).
+ Replace("4", string.Empty).
+ Replace("5", string.Empty).
+ Replace("6", string.Empty).
+ Replace("7", string.Empty).
+ Replace("8", string.Empty).
+ Replace("9", string.Empty).
+ Replace("a", string.Empty).
+ Replace("b", string.Empty).
+ Replace("c", string.Empty).
+ Replace("d", string.Empty).
+ Replace("e", string.Empty).
+ Replace("f", string.Empty).Length == 0)
+ {
+ if (s.StartsWith('#'))
+ {
+ return s;
+ }
+ return "#" + s;
+ }
+ return p;
+ }
+
+ private TimeCode GetTimeCode(string s)
+ {
+ var parts = s.Split(':', '.', ',');
+
+ int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate));
+ if (milliseconds > 999)
+ {
+ milliseconds = 999;
+ }
+
+ return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds);
+ }
+
+ private string ConvertToTimeString(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{DCinemaSmpte2010.MsToFramesMaxFrameRate(time.Milliseconds, _frameRate):00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DCinemaSmpte2010.cs b/src/libse/SubtitleFormats/DCinemaSmpte2010.cs
similarity index 98%
rename from libse/SubtitleFormats/DCinemaSmpte2010.cs
rename to src/libse/SubtitleFormats/DCinemaSmpte2010.cs
index 129429e3f..f72c8e305 100644
--- a/libse/SubtitleFormats/DCinemaSmpte2010.cs
+++ b/src/libse/SubtitleFormats/DCinemaSmpte2010.cs
@@ -1,854 +1,854 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Text;
-using System.Xml;
-using System.Xml.Schema;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DCinemaSmpte2010 : SubtitleFormat
- {
- //
- //
- // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e
- // 2001, A Space Odissey
- // This is a subtitle file
- // 2012-06-26T12:33:59.000-00:00
- // 1
- // fr
- // 25 1
- // 25
- // 00:00:00:00
- // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391
- //
- //
- // Hallo
- //
- //
-
- public string Errors { get; private set; }
-
- private double _frameRate = 24;
-
- public int Version { get; set; }
-
- public override string Extension => ".xml";
-
- public override string Name => "D-Cinema SMPTE 2010";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string xmlAsString = sb.ToString().Trim();
-
- if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2007/DCST") ||
- xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2014/DCST"))
- {
- return false;
- }
-
- if (xmlAsString.Contains(" 0;
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- return false;
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- Errors = null;
- var ss = Configuration.Settings.SubtitleSettings;
-
- if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate))
- {
- string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- double d1, d2;
- if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2))
- {
- _frameRate = d1 / d2;
- }
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine +
- " " + Environment.NewLine +
- " This is a subtitle file" + Environment.NewLine +
- " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine +
- " 1" + Environment.NewLine +
- " en" + Environment.NewLine +
- " 25 1" + Environment.NewLine +
- " 25" + Environment.NewLine +
- " 00:00:00:00 " + Environment.NewLine +
- " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.PreserveWhitespace = true;
- var nsmgr = new XmlNamespaceManager(xml.NameTable);
- nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI);
-
- if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle))
- {
- ss.CurrentDCinemaMovieTitle = title;
- }
-
- if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect))
- {
- Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true);
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:"))
- {
- ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid();
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId;
- xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber;
- xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage))
- {
- ss.CurrentDCinemaLanguage = "en";
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage;
- if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null)
- {
- if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 24) < 0.01)
- {
- ss.CurrentDCinemaEditRate = "24 1";
- ss.CurrentDCinemaTimeCodeRate = "24";
- }
- else
- {
- ss.CurrentDCinemaEditRate = "25 1";
- ss.CurrentDCinemaTimeCodeRate = "25";
- }
- }
- xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate;
- xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate;
- if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime))
- {
- ss.CurrentDCinemaStartTime = "00:00:00:00";
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime;
- xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri;
- int fontSize = ss.CurrentDCinemaFontSize;
- string loadedFontId = "Font1";
- if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId))
- {
- loadedFontId = ss.CurrentDCinemaFontId;
- }
-
- xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString();
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpperInvariant();
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect;
- xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpperInvariant();
-
- XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr);
- int no = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- if (p.Text != null)
- {
- XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst");
-
- XmlAttribute id = xml.CreateAttribute("SpotNumber");
- id.InnerText = (no + 1).ToString();
- subNode.Attributes.Append(id);
-
- XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime");
- fadeUpTime.InnerText = new TimeCode(FramesToMilliseconds(Configuration.Settings.SubtitleSettings.DCinemaFadeUpTime)).ToHHMMSSFF();
- subNode.Attributes.Append(fadeUpTime);
-
- XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime");
- fadeDownTime.InnerText = new TimeCode(FramesToMilliseconds(Configuration.Settings.SubtitleSettings.DCinemaFadeDownTime)).ToHHMMSSFF();
- subNode.Attributes.Append(fadeDownTime);
-
- XmlAttribute start = xml.CreateAttribute("TimeIn");
- start.InnerText = ConvertToTimeString(p.StartTime);
- subNode.Attributes.Append(start);
-
- XmlAttribute end = xml.CreateAttribute("TimeOut");
- end.InnerText = ConvertToTimeString(p.EndTime);
- subNode.Attributes.Append(end);
-
- bool alignLeft = p.Text.StartsWith("{\\a1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignRight = p.Text.StartsWith("{\\a3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignVTop = p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
-
- bool alignVCenter = p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a10}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
- p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal); // advanced sub station alpha
-
- string text = Utilities.RemoveSsaTags(p.Text);
-
- var lines = text.SplitToLines();
- int vPos = 1 + lines.Count * 7;
- int vPosFactor = (int)Math.Round(fontSize / 7.4);
- if (alignVTop)
- {
- vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
- }
- else if (alignVCenter)
- {
- vPos = (int)Math.Round((lines.Count * vPosFactor * -1) / 2.0);
- }
- else
- {
- vPos = (lines.Count * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
- }
-
- bool isItalic = false;
- int fontNo = 0;
- Stack fontColors = new Stack();
- foreach (string line in lines)
- {
- XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
-
- XmlAttribute vPosition = xml.CreateAttribute("Vposition");
- vPosition.InnerText = vPos.ToString();
- textNode.Attributes.Append(vPosition);
-
- XmlAttribute vAlign = xml.CreateAttribute("Valign");
- if (alignVTop)
- {
- vAlign.InnerText = "top";
- }
- else if (alignVCenter)
- {
- vAlign.InnerText = "center";
- }
- else
- {
- vAlign.InnerText = "bottom";
- }
-
- textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign);
-
- XmlAttribute hAlign = xml.CreateAttribute("Halign");
- if (alignLeft)
- {
- hAlign.InnerText = "left";
- }
- else if (alignRight)
- {
- hAlign.InnerText = "right";
- }
- else
- {
- hAlign.InnerText = "center";
- }
-
- textNode.Attributes.Append(hAlign);
-
- XmlAttribute direction = xml.CreateAttribute("Direction");
- direction.InnerText = "ltr";
- textNode.Attributes.Append(direction);
-
- int i = 0;
- var txt = new StringBuilder();
- var html = new StringBuilder();
- XmlNode nodeTemp = xml.CreateElement("temp");
- while (i < line.Length)
- {
- if (!isItalic && line.Substring(i).StartsWith("", StringComparison.Ordinal))
- {
- if (txt.Length > 0)
- {
- nodeTemp.InnerText = txt.ToString();
- html.Append(nodeTemp.InnerXml);
- txt.Clear();
- }
- isItalic = true;
- i += 2;
- }
- else if (isItalic && line.Substring(i).StartsWith("", StringComparison.Ordinal))
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
-
- if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("", StringComparison.Ordinal))
- {
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Pop();
- fontNode.Attributes.Append(fontColor);
- fontNo--;
- i += 7;
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- txt.Clear();
- }
- isItalic = false;
- i += 3;
- }
- else if (line.Substring(i).StartsWith("", StringComparison.Ordinal))
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Pop();
- fontNode.Attributes.Append(fontColor);
-
- if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("", StringComparison.Ordinal))
- {
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
- isItalic = false;
- i += 4;
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- txt.Clear();
- }
- fontNo--;
- i += 6;
- }
- else
- {
- txt.Append(line[i]);
- }
- i++;
- }
-
- if (fontNo > 0)
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Peek();
- fontNode.Attributes.Append(fontColor);
-
- if (isItalic)
- {
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
- }
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
- html.Append(fontNode.OuterXml);
- }
- else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + "");
- XmlNode fontNode = xml.CreateElement("dcst:Font");
- fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml;
- foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes)
- {
- XmlAttribute newA = xml.CreateAttribute(a.Name);
- newA.InnerText = a.InnerText;
- fontNode.Attributes.Append(newA);
- }
-
- XmlAttribute fontColor = xml.CreateAttribute("Color");
- fontColor.InnerText = fontColors.Peek();
- fontNode.Attributes.Append(fontColor);
-
- html.Clear();
- html.Append(fontNode.OuterXml);
- }
- }
- else if (isItalic)
- {
- if (txt.Length > 0)
- {
- XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
-
- XmlAttribute italic = xml.CreateAttribute("Italic");
- italic.InnerText = "yes";
- fontNode.Attributes.Append(italic);
-
- fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line);
- html.Append(fontNode.OuterXml);
- }
- }
- else
- {
- if (txt.Length > 0)
- {
- nodeTemp.InnerText = txt.ToString();
- html.Append(nodeTemp.InnerXml);
- }
- }
- textNode.InnerXml = html.ToString();
-
- subNode.AppendChild(textNode);
- if (alignVTop)
- {
- vPos += vPosFactor;
- }
- else
- {
- vPos -= vPosFactor;
- }
- }
- if (subNode.InnerXml.Length == 0)
- { // Empty text is just one space
- XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
- textNode.InnerXml = " ";
- subNode.AppendChild(textNode);
-
- XmlAttribute vPosition = xml.CreateAttribute("Vposition");
- vPosition.InnerText = vPos.ToString();
- textNode.Attributes.Append(vPosition);
-
- XmlAttribute vAlign = xml.CreateAttribute("Valign");
- vAlign.InnerText = "bottom";
- textNode.Attributes.Append(vAlign);
- }
- mainListFont.AppendChild(subNode);
- no++;
- }
- }
- string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty);
-
- const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2010-DCST.xsd.gz";
- System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
- Stream strm = asm.GetManifestResourceStream(res);
- if (strm != null)
- {
- try
- {
- var xmld = new XmlDocument();
- var rdr = new StreamReader(strm);
- var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress);
- xmld.LoadXml(result);
- using (var xr = XmlReader.Create(zip))
- {
- xmld.Schemas.Add(null, xr);
- xmld.Validate(ValidationCallBack);
- }
- }
- catch (Exception exception)
- {
- Errors = "Error validating xml via SMPTE-428-7-2010-DCST.xsd: " + exception.Message;
- }
- }
- return FixDcsTextSameLine(result);
- }
-
- ///
- /// All space characters present inside the content of a Text element shall be rendered
- ///
- internal static string FixDcsTextSameLine(string xml)
- {
- int index = xml.IndexOf(" 0 && endIndex > 0)
- {
- endIndex = xml.IndexOf("", index, StringComparison.Ordinal);
- if (endIndex > 0)
- {
- var part = xml.Substring(index, endIndex - index);
- if (part.Contains(Environment.NewLine))
- {
- part = part.Replace(Environment.NewLine, " ");
- while (part.Contains(" "))
- {
- part = part.Replace(" ", " ");
- }
- part = part.Replace("> <", "><");
- }
- xml = xml.Remove(index, endIndex - index).Insert(index, part);
- index = xml.IndexOf(" lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0)
- {
- pText.AppendLine();
- }
-
- lastVPosition = vPosition;
- }
- }
-
- bool alignLeft = false;
- bool alignRight = false;
- bool alignVTop = false;
- bool alignVCenter = false;
- if (innerNode.Attributes["Halign"] != null)
- {
- string hAlign = innerNode.Attributes["Halign"].InnerText;
- if (hAlign == "left")
- {
- alignLeft = true;
- }
- else if (hAlign == "right")
- {
- alignRight = true;
- }
- }
-
- if (innerNode.Attributes["Valign"] != null)
- {
- string hAlign = innerNode.Attributes["Valign"].InnerText;
- if (hAlign == "top")
- {
- alignVTop = true;
- }
- else if (hAlign == "center")
- {
- alignVCenter = true;
- }
- }
-
- if (alignLeft || alignRight || alignVCenter || alignVTop)
- {
- if (!pText.ToString().StartsWith("{\\an", StringComparison.Ordinal))
- {
- string pre = string.Empty;
- if (alignVTop)
- {
- if (alignLeft)
- {
- pre = "{\\an7}";
- }
- else if (alignRight)
- {
- pre = "{\\an9}";
- }
- else
- {
- pre = "{\\an8}";
- }
- }
- else if (alignVCenter)
- {
- if (alignLeft)
- {
- pre = "{\\an4}";
- }
- else if (alignRight)
- {
- pre = "{\\an6}";
- }
- else
- {
- pre = "{\\an5}";
- }
- }
- else
- {
- if (alignLeft)
- {
- pre = "{\\an1}";
- }
- else if (alignRight)
- {
- pre = "{\\an3}";
- }
- }
-
- string temp = pre + pText;
- pText.Clear();
- pText.Append(temp);
- }
- }
-
- if (innerNode.ChildNodes.Count == 0)
- {
- pText.Append(innerNode.InnerText);
- }
- else
- {
- foreach (XmlNode innerInnerNode in innerNode)
- {
- if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null &&
- innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
- {
- if (innerInnerNode.Attributes["Color"] != null)
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- else
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- }
- else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null)
- {
- if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- else
- {
- pText.Append("" + innerInnerNode.InnerText + "");
- }
- }
- else
- {
- pText.Append(innerInnerNode.InnerText);
- }
- }
- }
- }
- else
- {
- pText.Append(innerNode.InnerText);
- }
- }
- string start = node.Attributes["TimeIn"].InnerText;
- string end = node.Attributes["TimeOut"].InnerText;
-
- if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) &&
- !pText.ToString().Contains(""))
- {
- string text = pText.ToString();
- if (text.StartsWith("{\\an", StringComparison.Ordinal) && text.Length > 6)
- {
- text = text.Insert(6, "") + "";
- }
- else
- {
- text = "" + text + "";
- }
-
- pText = new StringBuilder(text);
- }
-
- subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString()));
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
-
- if (subtitle.Paragraphs.Count > 0)
- {
- subtitle.Header = xml.OuterXml; // save id/language/font for later use
- }
-
- subtitle.Renumber();
- }
-
- private TimeCode GetTimeCode(string s)
- {
- var parts = s.Split(new[] { ':', '.', ',' });
-
- int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate));
- if (milliseconds > 999)
- {
- milliseconds = 999;
- }
-
- return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds);
- }
-
- public static int MsToFramesMaxFrameRate(double milliseconds, double frameRate)
- {
- int frames = (int)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
- if (frames >= Configuration.Settings.General.CurrentFrameRate)
- {
- frames = (int)(frameRate - 0.01);
- }
-
- return frames;
- }
-
- private string ConvertToTimeString(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MsToFramesMaxFrameRate(time.Milliseconds, _frameRate):00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+using System.Xml;
+using System.Xml.Schema;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DCinemaSmpte2010 : SubtitleFormat
+ {
+ //
+ //
+ // urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e
+ // 2001, A Space Odissey
+ // This is a subtitle file
+ // 2012-06-26T12:33:59.000-00:00
+ // 1
+ // fr
+ // 25 1
+ // 25
+ // 00:00:00:00
+ // urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391
+ //
+ //
+ // Hallo
+ //
+ //
+
+ public string Errors { get; private set; }
+
+ private double _frameRate = 24;
+
+ public int Version { get; set; }
+
+ public override string Extension => ".xml";
+
+ public override string Name => "D-Cinema SMPTE 2010";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string xmlAsString = sb.ToString().Trim();
+
+ if (xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2007/DCST") ||
+ xmlAsString.Contains("http://www.smpte-ra.org/schemas/428-7/2014/DCST"))
+ {
+ return false;
+ }
+
+ if (xmlAsString.Contains(" 0;
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ Errors = null;
+ var ss = Configuration.Settings.SubtitleSettings;
+
+ if (!string.IsNullOrEmpty(ss.CurrentDCinemaEditRate))
+ {
+ string[] temp = ss.CurrentDCinemaEditRate.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ double d1, d2;
+ if (temp.Length == 2 && double.TryParse(temp[0], out d1) && double.TryParse(temp[1], out d2))
+ {
+ _frameRate = d1 / d2;
+ }
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ " urn:uuid:7be835a3-cfb4-43d0-bb4b-f0b4c95e962e" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " This is a subtitle file" + Environment.NewLine +
+ " 2012-06-26T12:33:59.000-00:00" + Environment.NewLine +
+ " 1" + Environment.NewLine +
+ " en" + Environment.NewLine +
+ " 25 1" + Environment.NewLine +
+ " 25" + Environment.NewLine +
+ " 00:00:00:00 " + Environment.NewLine +
+ " urn:uuid:3dec6dc0-39d0-498d-97d0-928d2eb78391" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.PreserveWhitespace = true;
+ var nsmgr = new XmlNamespaceManager(xml.NameTable);
+ nsmgr.AddNamespace("dcst", xml.DocumentElement.NamespaceURI);
+
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaMovieTitle))
+ {
+ ss.CurrentDCinemaMovieTitle = title;
+ }
+
+ if (ss.CurrentDCinemaFontSize == 0 || string.IsNullOrEmpty(ss.CurrentDCinemaFontEffect))
+ {
+ Configuration.Settings.SubtitleSettings.InitializeDCinameSettings(true);
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:ContentTitleText", nsmgr).InnerText = ss.CurrentDCinemaMovieTitle;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaSubtitleId) || !ss.CurrentDCinemaSubtitleId.StartsWith("urn:uuid:"))
+ {
+ ss.CurrentDCinemaSubtitleId = "urn:uuid:" + Guid.NewGuid();
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:Id", nsmgr).InnerText = ss.CurrentDCinemaSubtitleId;
+ xml.DocumentElement.SelectSingleNode("dcst:ReelNumber", nsmgr).InnerText = ss.CurrentDCinemaReelNumber;
+ xml.DocumentElement.SelectSingleNode("dcst:IssueDate", nsmgr).InnerText = ss.CurrentDCinemaIssueDate;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaLanguage))
+ {
+ ss.CurrentDCinemaLanguage = "en";
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:Language", nsmgr).InnerText = ss.CurrentDCinemaLanguage;
+ if (ss.CurrentDCinemaEditRate == null && ss.CurrentDCinemaTimeCodeRate == null)
+ {
+ if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 24) < 0.01)
+ {
+ ss.CurrentDCinemaEditRate = "24 1";
+ ss.CurrentDCinemaTimeCodeRate = "24";
+ }
+ else
+ {
+ ss.CurrentDCinemaEditRate = "25 1";
+ ss.CurrentDCinemaTimeCodeRate = "25";
+ }
+ }
+ xml.DocumentElement.SelectSingleNode("dcst:EditRate", nsmgr).InnerText = ss.CurrentDCinemaEditRate;
+ xml.DocumentElement.SelectSingleNode("dcst:TimeCodeRate", nsmgr).InnerText = ss.CurrentDCinemaTimeCodeRate;
+ if (string.IsNullOrEmpty(ss.CurrentDCinemaStartTime))
+ {
+ ss.CurrentDCinemaStartTime = "00:00:00:00";
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:StartTime", nsmgr).InnerText = ss.CurrentDCinemaStartTime;
+ xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).InnerText = ss.CurrentDCinemaFontUri;
+ int fontSize = ss.CurrentDCinemaFontSize;
+ string loadedFontId = "Font1";
+ if (!string.IsNullOrEmpty(ss.CurrentDCinemaFontId))
+ {
+ loadedFontId = ss.CurrentDCinemaFontId;
+ }
+
+ xml.DocumentElement.SelectSingleNode("dcst:LoadFont", nsmgr).Attributes["ID"].Value = loadedFontId;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Size"].Value = fontSize.ToString();
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Color"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontColor).TrimStart('#').ToUpperInvariant();
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["ID"].Value = loadedFontId;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["Effect"].Value = ss.CurrentDCinemaFontEffect;
+ xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr).Attributes["EffectColor"].Value = "FF" + Utilities.ColorToHex(ss.CurrentDCinemaFontEffectColor).TrimStart('#').ToUpperInvariant();
+
+ XmlNode mainListFont = xml.DocumentElement.SelectSingleNode("dcst:SubtitleList/dcst:Font", nsmgr);
+ int no = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ if (p.Text != null)
+ {
+ XmlNode subNode = xml.CreateElement("dcst:Subtitle", "dcst");
+
+ XmlAttribute id = xml.CreateAttribute("SpotNumber");
+ id.InnerText = (no + 1).ToString();
+ subNode.Attributes.Append(id);
+
+ XmlAttribute fadeUpTime = xml.CreateAttribute("FadeUpTime");
+ fadeUpTime.InnerText = new TimeCode(FramesToMilliseconds(Configuration.Settings.SubtitleSettings.DCinemaFadeUpTime)).ToHHMMSSFF();
+ subNode.Attributes.Append(fadeUpTime);
+
+ XmlAttribute fadeDownTime = xml.CreateAttribute("FadeDownTime");
+ fadeDownTime.InnerText = new TimeCode(FramesToMilliseconds(Configuration.Settings.SubtitleSettings.DCinemaFadeDownTime)).ToHHMMSSFF();
+ subNode.Attributes.Append(fadeDownTime);
+
+ XmlAttribute start = xml.CreateAttribute("TimeIn");
+ start.InnerText = ConvertToTimeString(p.StartTime);
+ subNode.Attributes.Append(start);
+
+ XmlAttribute end = xml.CreateAttribute("TimeOut");
+ end.InnerText = ConvertToTimeString(p.EndTime);
+ subNode.Attributes.Append(end);
+
+ bool alignLeft = p.Text.StartsWith("{\\a1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an7}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignRight = p.Text.StartsWith("{\\a3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignVTop = p.Text.StartsWith("{\\a5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a6}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a7}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an9}", StringComparison.Ordinal); // advanced sub station alpha
+
+ bool alignVCenter = p.Text.StartsWith("{\\a9}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a10}", StringComparison.Ordinal) || p.Text.StartsWith("{\\a11}", StringComparison.Ordinal) || // sub station alpha
+ p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) || p.Text.StartsWith("{\\an6}", StringComparison.Ordinal); // advanced sub station alpha
+
+ string text = Utilities.RemoveSsaTags(p.Text);
+
+ var lines = text.SplitToLines();
+ int vPos = 1 + lines.Count * 7;
+ int vPosFactor = (int)Math.Round(fontSize / 7.4);
+ if (alignVTop)
+ {
+ vPos = Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
+ }
+ else if (alignVCenter)
+ {
+ vPos = (int)Math.Round((lines.Count * vPosFactor * -1) / 2.0);
+ }
+ else
+ {
+ vPos = (lines.Count * vPosFactor) - vPosFactor + Configuration.Settings.SubtitleSettings.DCinemaBottomMargin; // Bottom margin is normally 8
+ }
+
+ bool isItalic = false;
+ int fontNo = 0;
+ Stack fontColors = new Stack();
+ foreach (string line in lines)
+ {
+ XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
+
+ XmlAttribute vPosition = xml.CreateAttribute("Vposition");
+ vPosition.InnerText = vPos.ToString();
+ textNode.Attributes.Append(vPosition);
+
+ XmlAttribute vAlign = xml.CreateAttribute("Valign");
+ if (alignVTop)
+ {
+ vAlign.InnerText = "top";
+ }
+ else if (alignVCenter)
+ {
+ vAlign.InnerText = "center";
+ }
+ else
+ {
+ vAlign.InnerText = "bottom";
+ }
+
+ textNode.Attributes.Append(vAlign); textNode.Attributes.Append(vAlign);
+
+ XmlAttribute hAlign = xml.CreateAttribute("Halign");
+ if (alignLeft)
+ {
+ hAlign.InnerText = "left";
+ }
+ else if (alignRight)
+ {
+ hAlign.InnerText = "right";
+ }
+ else
+ {
+ hAlign.InnerText = "center";
+ }
+
+ textNode.Attributes.Append(hAlign);
+
+ XmlAttribute direction = xml.CreateAttribute("Direction");
+ direction.InnerText = "ltr";
+ textNode.Attributes.Append(direction);
+
+ int i = 0;
+ var txt = new StringBuilder();
+ var html = new StringBuilder();
+ XmlNode nodeTemp = xml.CreateElement("temp");
+ while (i < line.Length)
+ {
+ if (!isItalic && line.Substring(i).StartsWith("", StringComparison.Ordinal))
+ {
+ if (txt.Length > 0)
+ {
+ nodeTemp.InnerText = txt.ToString();
+ html.Append(nodeTemp.InnerXml);
+ txt.Clear();
+ }
+ isItalic = true;
+ i += 2;
+ }
+ else if (isItalic && line.Substring(i).StartsWith("", StringComparison.Ordinal))
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+
+ if (line.Length > i + 5 && line.Substring(i + 4).StartsWith("", StringComparison.Ordinal))
+ {
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Pop();
+ fontNode.Attributes.Append(fontColor);
+ fontNo--;
+ i += 7;
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ txt.Clear();
+ }
+ isItalic = false;
+ i += 3;
+ }
+ else if (line.Substring(i).StartsWith("", StringComparison.Ordinal))
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Pop();
+ fontNode.Attributes.Append(fontColor);
+
+ if (line.Length > i + 9 && line.Substring(i + 7).StartsWith("", StringComparison.Ordinal))
+ {
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+ isItalic = false;
+ i += 4;
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ txt.Clear();
+ }
+ fontNo--;
+ i += 6;
+ }
+ else
+ {
+ txt.Append(line[i]);
+ }
+ i++;
+ }
+
+ if (fontNo > 0)
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Peek();
+ fontNode.Attributes.Append(fontColor);
+
+ if (isItalic)
+ {
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+ }
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(txt.ToString());
+ html.Append(fontNode.OuterXml);
+ }
+ else if (html.Length > 0 && html.ToString().StartsWith("" + html.ToString().Replace("dcst:Font", "Font") + "");
+ XmlNode fontNode = xml.CreateElement("dcst:Font");
+ fontNode.InnerXml = temp.DocumentElement.SelectSingleNode("Font").InnerXml;
+ foreach (XmlAttribute a in temp.DocumentElement.SelectSingleNode("Font").Attributes)
+ {
+ XmlAttribute newA = xml.CreateAttribute(a.Name);
+ newA.InnerText = a.InnerText;
+ fontNode.Attributes.Append(newA);
+ }
+
+ XmlAttribute fontColor = xml.CreateAttribute("Color");
+ fontColor.InnerText = fontColors.Peek();
+ fontNode.Attributes.Append(fontColor);
+
+ html.Clear();
+ html.Append(fontNode.OuterXml);
+ }
+ }
+ else if (isItalic)
+ {
+ if (txt.Length > 0)
+ {
+ XmlNode fontNode = xml.CreateElement("dcst:Font", "dcst");
+
+ XmlAttribute italic = xml.CreateAttribute("Italic");
+ italic.InnerText = "yes";
+ fontNode.Attributes.Append(italic);
+
+ fontNode.InnerText = HtmlUtil.RemoveHtmlTags(line);
+ html.Append(fontNode.OuterXml);
+ }
+ }
+ else
+ {
+ if (txt.Length > 0)
+ {
+ nodeTemp.InnerText = txt.ToString();
+ html.Append(nodeTemp.InnerXml);
+ }
+ }
+ textNode.InnerXml = html.ToString();
+
+ subNode.AppendChild(textNode);
+ if (alignVTop)
+ {
+ vPos += vPosFactor;
+ }
+ else
+ {
+ vPos -= vPosFactor;
+ }
+ }
+ if (subNode.InnerXml.Length == 0)
+ { // Empty text is just one space
+ XmlNode textNode = xml.CreateElement("dcst:Text", "dcst");
+ textNode.InnerXml = " ";
+ subNode.AppendChild(textNode);
+
+ XmlAttribute vPosition = xml.CreateAttribute("Vposition");
+ vPosition.InnerText = vPos.ToString();
+ textNode.Attributes.Append(vPosition);
+
+ XmlAttribute vAlign = xml.CreateAttribute("Valign");
+ vAlign.InnerText = "bottom";
+ textNode.Attributes.Append(vAlign);
+ }
+ mainListFont.AppendChild(subNode);
+ no++;
+ }
+ }
+ string result = ToUtf8XmlString(xml).Replace("encoding=\"utf-8\"", "encoding=\"UTF-8\"").Replace(" xmlns:dcst=\"dcst\"", string.Empty);
+
+ const string res = "Nikse.SubtitleEdit.Resources.SMPTE-428-7-2010-DCST.xsd.gz";
+ System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
+ Stream strm = asm.GetManifestResourceStream(res);
+ if (strm != null)
+ {
+ try
+ {
+ var xmld = new XmlDocument();
+ var rdr = new StreamReader(strm);
+ var zip = new GZipStream(rdr.BaseStream, CompressionMode.Decompress);
+ xmld.LoadXml(result);
+ using (var xr = XmlReader.Create(zip))
+ {
+ xmld.Schemas.Add(null, xr);
+ xmld.Validate(ValidationCallBack);
+ }
+ }
+ catch (Exception exception)
+ {
+ Errors = "Error validating xml via SMPTE-428-7-2010-DCST.xsd: " + exception.Message;
+ }
+ }
+ return FixDcsTextSameLine(result);
+ }
+
+ ///
+ /// All space characters present inside the content of a Text element shall be rendered
+ ///
+ internal static string FixDcsTextSameLine(string xml)
+ {
+ int index = xml.IndexOf(" 0 && endIndex > 0)
+ {
+ endIndex = xml.IndexOf("", index, StringComparison.Ordinal);
+ if (endIndex > 0)
+ {
+ var part = xml.Substring(index, endIndex - index);
+ if (part.Contains(Environment.NewLine))
+ {
+ part = part.Replace(Environment.NewLine, " ");
+ while (part.Contains(" "))
+ {
+ part = part.Replace(" ", " ");
+ }
+ part = part.Replace("> <", "><");
+ }
+ xml = xml.Remove(index, endIndex - index).Insert(index, part);
+ index = xml.IndexOf(" lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(sb.ToString().Replace(" 0 && lastVPosition.Length > 0)
+ {
+ pText.AppendLine();
+ }
+
+ lastVPosition = vPosition;
+ }
+ }
+
+ bool alignLeft = false;
+ bool alignRight = false;
+ bool alignVTop = false;
+ bool alignVCenter = false;
+ if (innerNode.Attributes["Halign"] != null)
+ {
+ string hAlign = innerNode.Attributes["Halign"].InnerText;
+ if (hAlign == "left")
+ {
+ alignLeft = true;
+ }
+ else if (hAlign == "right")
+ {
+ alignRight = true;
+ }
+ }
+
+ if (innerNode.Attributes["Valign"] != null)
+ {
+ string hAlign = innerNode.Attributes["Valign"].InnerText;
+ if (hAlign == "top")
+ {
+ alignVTop = true;
+ }
+ else if (hAlign == "center")
+ {
+ alignVCenter = true;
+ }
+ }
+
+ if (alignLeft || alignRight || alignVCenter || alignVTop)
+ {
+ if (!pText.ToString().StartsWith("{\\an", StringComparison.Ordinal))
+ {
+ string pre = string.Empty;
+ if (alignVTop)
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an7}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an9}";
+ }
+ else
+ {
+ pre = "{\\an8}";
+ }
+ }
+ else if (alignVCenter)
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an4}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an6}";
+ }
+ else
+ {
+ pre = "{\\an5}";
+ }
+ }
+ else
+ {
+ if (alignLeft)
+ {
+ pre = "{\\an1}";
+ }
+ else if (alignRight)
+ {
+ pre = "{\\an3}";
+ }
+ }
+
+ string temp = pre + pText;
+ pText.Clear();
+ pText.Append(temp);
+ }
+ }
+
+ if (innerNode.ChildNodes.Count == 0)
+ {
+ pText.Append(innerNode.InnerText);
+ }
+ else
+ {
+ foreach (XmlNode innerInnerNode in innerNode)
+ {
+ if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Italic"] != null &&
+ innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
+ {
+ if (innerInnerNode.Attributes["Color"] != null)
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ else
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ }
+ else if (innerInnerNode.Name == "Font" && innerInnerNode.Attributes["Color"] != null)
+ {
+ if (innerInnerNode.Attributes["Italic"] != null && innerInnerNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase))
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ else
+ {
+ pText.Append("" + innerInnerNode.InnerText + "");
+ }
+ }
+ else
+ {
+ pText.Append(innerInnerNode.InnerText);
+ }
+ }
+ }
+ }
+ else
+ {
+ pText.Append(innerNode.InnerText);
+ }
+ }
+ string start = node.Attributes["TimeIn"].InnerText;
+ string end = node.Attributes["TimeOut"].InnerText;
+
+ if (node.ParentNode.Name == "Font" && node.ParentNode.Attributes["Italic"] != null && node.ParentNode.Attributes["Italic"].InnerText.Equals("yes", StringComparison.OrdinalIgnoreCase) &&
+ !pText.ToString().Contains(""))
+ {
+ string text = pText.ToString();
+ if (text.StartsWith("{\\an", StringComparison.Ordinal) && text.Length > 6)
+ {
+ text = text.Insert(6, "") + "";
+ }
+ else
+ {
+ text = "" + text + "";
+ }
+
+ pText = new StringBuilder(text);
+ }
+
+ subtitle.Paragraphs.Add(new Paragraph(GetTimeCode(start), GetTimeCode(end), pText.ToString()));
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ subtitle.Header = xml.OuterXml; // save id/language/font for later use
+ }
+
+ subtitle.Renumber();
+ }
+
+ private TimeCode GetTimeCode(string s)
+ {
+ var parts = s.Split(new[] { ':', '.', ',' });
+
+ int milliseconds = (int)Math.Round(int.Parse(parts[3]) * (TimeCode.BaseUnit / _frameRate));
+ if (milliseconds > 999)
+ {
+ milliseconds = 999;
+ }
+
+ return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), milliseconds);
+ }
+
+ public static int MsToFramesMaxFrameRate(double milliseconds, double frameRate)
+ {
+ int frames = (int)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
+ if (frames >= Configuration.Settings.General.CurrentFrameRate)
+ {
+ frames = (int)(frameRate - 0.01);
+ }
+
+ return frames;
+ }
+
+ private string ConvertToTimeString(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MsToFramesMaxFrameRate(time.Milliseconds, _frameRate):00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DCinemaSmpte2014.cs b/src/libse/SubtitleFormats/DCinemaSmpte2014.cs
similarity index 100%
rename from libse/SubtitleFormats/DCinemaSmpte2014.cs
rename to src/libse/SubtitleFormats/DCinemaSmpte2014.cs
diff --git a/libse/SubtitleFormats/DigiBeta.cs b/src/libse/SubtitleFormats/DigiBeta.cs
similarity index 97%
rename from libse/SubtitleFormats/DigiBeta.cs
rename to src/libse/SubtitleFormats/DigiBeta.cs
index a422891f6..81202a2be 100644
--- a/libse/SubtitleFormats/DigiBeta.cs
+++ b/src/libse/SubtitleFormats/DigiBeta.cs
@@ -1,72 +1,72 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DigiBeta : SubtitleFormat
- {
- private static readonly Regex RegexTimeCode = new Regex(@"^\d\d \d\d \d\d \d\d\t\d\d \d\d \d\d \d\d\t", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "DigiBeta";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- //10 01 37 23 10 01 42 01 Makkhi (newline is TAB)
- const string paragraphWriteFormat = "{0}\t{1}\t{2}";
-
- var sb = new StringBuilder();
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\t")));
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- subtitle.Paragraphs.Clear();
- foreach (string line in lines)
- {
- if (RegexTimeCode.IsMatch(line) && line.Length > 24)
- {
- string[] parts = line.Substring(0, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 4)
- {
- try
- {
- var start = DecodeTimeCodeFramesFourParts(parts);
- parts = line.Substring(12, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- var end = DecodeTimeCodeFramesFourParts(parts);
- var paragraph = new Paragraph
- {
- StartTime = start,
- EndTime = end,
- Text = line.Substring(24).Trim().Replace("\t", Environment.NewLine)
- };
-
- subtitle.Paragraphs.Add(paragraph);
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- }
- subtitle.Renumber();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $"{time.Hours:00} {time.Minutes:00} {time.Seconds:00} {MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DigiBeta : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCode = new Regex(@"^\d\d \d\d \d\d \d\d\t\d\d \d\d \d\d \d\d\t", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "DigiBeta";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ //10 01 37 23 10 01 42 01 Makkhi (newline is TAB)
+ const string paragraphWriteFormat = "{0}\t{1}\t{2}";
+
+ var sb = new StringBuilder();
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), p.Text.Replace(Environment.NewLine, "\t")));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ subtitle.Paragraphs.Clear();
+ foreach (string line in lines)
+ {
+ if (RegexTimeCode.IsMatch(line) && line.Length > 24)
+ {
+ string[] parts = line.Substring(0, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 4)
+ {
+ try
+ {
+ var start = DecodeTimeCodeFramesFourParts(parts);
+ parts = line.Substring(12, 11).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ var end = DecodeTimeCodeFramesFourParts(parts);
+ var paragraph = new Paragraph
+ {
+ StartTime = start,
+ EndTime = end,
+ Text = line.Substring(24).Trim().Replace("\t", Environment.NewLine)
+ };
+
+ subtitle.Paragraphs.Add(paragraph);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $"{time.Hours:00} {time.Minutes:00} {time.Seconds:00} {MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DlDd.cs b/src/libse/SubtitleFormats/DlDd.cs
similarity index 100%
rename from libse/SubtitleFormats/DlDd.cs
rename to src/libse/SubtitleFormats/DlDd.cs
diff --git a/libse/SubtitleFormats/Dost.cs b/src/libse/SubtitleFormats/Dost.cs
similarity index 97%
rename from libse/SubtitleFormats/Dost.cs
rename to src/libse/SubtitleFormats/Dost.cs
index 9c3db4bbb..1028918d9 100644
--- a/libse/SubtitleFormats/Dost.cs
+++ b/src/libse/SubtitleFormats/Dost.cs
@@ -1,104 +1,104 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Dost : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t", RegexOptions.Compiled);
-
- public override string Extension => ".dost";
-
- public override string Name => "DOST";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- foreach (string line in lines)
- {
- sb.AppendLine(line);
- }
-
- if (!sb.ToString().Contains(Environment.NewLine + "NO\tINTIME"))
- {
- return false;
- }
-
- if (!sb.ToString().Contains("$FORMAT"))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not implemented";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- //0001 01:25:59:21 01:26:00:20 0 0 BK02-total_0001.png 0 0
- Paragraph p = null;
- subtitle.Paragraphs.Clear();
- _errorCount = 0;
- foreach (string line in lines)
- {
- string s = line;
- if (RegexTimeCodes.IsMatch(s))
- {
- var temp = s.Split('\t');
- if (temp.Length > 7)
- {
- string start = temp[1];
- string end = temp[2];
- string text = temp[5];
- try
- {
- p = new Paragraph(DecodeTimeCodeFramesFourParts(start.Split(':')), DecodeTimeCodeFramesFourParts(end.Split(':')), text);
- subtitle.Paragraphs.Add(p);
- }
- catch (Exception exception)
- {
- _errorCount++;
- System.Diagnostics.Debug.WriteLine(exception.Message);
- }
- }
- }
- else if (line.StartsWith("$DROP=", StringComparison.Ordinal))
- {
- s = s.Remove(0, "$DROP=".Length);
- int frameRate;
- if (int.TryParse(s, out frameRate))
- {
- double f = frameRate / TimeCode.BaseUnit;
- if (f > 10 && f < 500)
- {
- Configuration.Settings.General.CurrentFrameRate = f;
- }
-
- if (BatchSourceFrameRate.HasValue)
- {
- Configuration.Settings.General.CurrentFrameRate = BatchSourceFrameRate.Value;
- }
- }
- }
- else if (string.IsNullOrWhiteSpace(line) || line.StartsWith('$'))
- {
- // skip empty lines or lines starting with $
- }
- else if (!string.IsNullOrWhiteSpace(line) && p != null)
- {
- _errorCount++;
- }
- }
-
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Dost : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d\d\d\t\d\d:\d\d:\d\d:\d\d\t\d\d:\d\d:\d\d:\d\d\t", RegexOptions.Compiled);
+
+ public override string Extension => ".dost";
+
+ public override string Name => "DOST";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ foreach (string line in lines)
+ {
+ sb.AppendLine(line);
+ }
+
+ if (!sb.ToString().Contains(Environment.NewLine + "NO\tINTIME"))
+ {
+ return false;
+ }
+
+ if (!sb.ToString().Contains("$FORMAT"))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not implemented";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ //0001 01:25:59:21 01:26:00:20 0 0 BK02-total_0001.png 0 0
+ Paragraph p = null;
+ subtitle.Paragraphs.Clear();
+ _errorCount = 0;
+ foreach (string line in lines)
+ {
+ string s = line;
+ if (RegexTimeCodes.IsMatch(s))
+ {
+ var temp = s.Split('\t');
+ if (temp.Length > 7)
+ {
+ string start = temp[1];
+ string end = temp[2];
+ string text = temp[5];
+ try
+ {
+ p = new Paragraph(DecodeTimeCodeFramesFourParts(start.Split(':')), DecodeTimeCodeFramesFourParts(end.Split(':')), text);
+ subtitle.Paragraphs.Add(p);
+ }
+ catch (Exception exception)
+ {
+ _errorCount++;
+ System.Diagnostics.Debug.WriteLine(exception.Message);
+ }
+ }
+ }
+ else if (line.StartsWith("$DROP=", StringComparison.Ordinal))
+ {
+ s = s.Remove(0, "$DROP=".Length);
+ int frameRate;
+ if (int.TryParse(s, out frameRate))
+ {
+ double f = frameRate / TimeCode.BaseUnit;
+ if (f > 10 && f < 500)
+ {
+ Configuration.Settings.General.CurrentFrameRate = f;
+ }
+
+ if (BatchSourceFrameRate.HasValue)
+ {
+ Configuration.Settings.General.CurrentFrameRate = BatchSourceFrameRate.Value;
+ }
+ }
+ }
+ else if (string.IsNullOrWhiteSpace(line) || line.StartsWith('$'))
+ {
+ // skip empty lines or lines starting with $
+ }
+ else if (!string.IsNullOrWhiteSpace(line) && p != null)
+ {
+ _errorCount++;
+ }
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DvSubtitle.cs b/src/libse/SubtitleFormats/DvSubtitle.cs
similarity index 100%
rename from libse/SubtitleFormats/DvSubtitle.cs
rename to src/libse/SubtitleFormats/DvSubtitle.cs
diff --git a/libse/SubtitleFormats/DvdStudioPro.cs b/src/libse/SubtitleFormats/DvdStudioPro.cs
similarity index 97%
rename from libse/SubtitleFormats/DvdStudioPro.cs
rename to src/libse/SubtitleFormats/DvdStudioPro.cs
index 3031aa419..943725f1b 100644
--- a/libse/SubtitleFormats/DvdStudioPro.cs
+++ b/src/libse/SubtitleFormats/DvdStudioPro.cs
@@ -1,327 +1,327 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DvdStudioPro : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+[:;]\d+\t,\t\d+:\d+:\d+[:;]\d+\t,\t.*$", RegexOptions.Compiled);
-
- public override string Extension => ".STL";
-
- public override string Name => "DVD Studio Pro";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string paragraphWriteFormat = "{0}\t,\t{1}\t,\t{2}\r\n";
- const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
- var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
-
- var lastVerticalAlign = "$VertAlign = Bottom";
- var lastHorizontalcalAlign = "$HorzAlign = Center";
- var sb = new StringBuilder();
- sb.AppendLine(header);
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
- string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
- sb = ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
- sb.AppendFormat(paragraphWriteFormat, startTime, endTime, EncodeStyles(p.Text));
- }
- return sb.ToString().Trim();
- }
-
- internal static StringBuilder ToTextAlignment(Paragraph p, StringBuilder sb, ref string lastVerticalAlign, ref string lastHorizontalAlign)
- {
- string verticalAlign;
- string horizontalAlign;
- bool verticalTopAlign = p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an9}", StringComparison.Ordinal);
- bool verticalCenterAlign = p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an6}", StringComparison.Ordinal);
- if (verticalTopAlign)
- {
- verticalAlign = "$VertAlign = Top";
- }
- else if (verticalCenterAlign)
- {
- verticalAlign = "$VertAlign = Center";
- }
- else
- {
- verticalAlign = "$VertAlign = Bottom";
- }
-
- if (lastVerticalAlign != verticalAlign)
- {
- sb.AppendLine(verticalAlign);
- }
-
- bool horizontalLeftAlign = p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an7}", StringComparison.Ordinal);
- bool horizontalRightAlign = p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) ||
- p.Text.StartsWith("{\\an9}", StringComparison.Ordinal);
- if (horizontalLeftAlign)
- {
- horizontalAlign = "$HorzAlign = Left";
- }
- else if (horizontalRightAlign)
- {
- horizontalAlign = "$HorzAlign = Right";
- }
- else
- {
- horizontalAlign = "$HorzAlign = Center";
- }
-
- if (lastHorizontalAlign != horizontalAlign)
- {
- sb.AppendLine(horizontalAlign);
- }
-
- lastVerticalAlign = verticalAlign;
- lastHorizontalAlign = horizontalAlign;
- return sb;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- int number = 0;
- var verticalAlign = "$VertAlign=Bottom";
- var horizontalAlign = "$HorzAlign=Center";
- bool italicOn = false;
- bool boldOn = false;
- bool underlineOn = false;
-
- foreach (string line in lines)
- {
- if (!string.IsNullOrWhiteSpace(line) && line[0] != '$')
- {
- if (RegexTimeCodes.Match(line).Success)
- {
- string[] threePart = line.Split(new[] { "\t,\t" }, StringSplitOptions.None);
- var p = new Paragraph();
- if (threePart.Length == 3 &&
- GetTimeCode(p.StartTime, threePart[0]) &&
- GetTimeCode(p.EndTime, threePart[1]))
- {
- number++;
- p.Number = number;
- p.Text = threePart[2].TrimEnd().Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
- p.Text = DecodeStyles(p.Text);
- if (italicOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (boldOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (underlineOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- p.Text = GetAlignment(verticalAlign, horizontalAlign) + p.Text;
- subtitle.Paragraphs.Add(p);
- }
- }
- else
- {
- _errorCount++;
- }
- }
- else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
- {
- verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
- {
- horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = false;
- }
- }
- }
-
- internal static string GetAlignment(string verticalAlign, string horizontalAlign)
- {
- if (verticalAlign.Equals("$VertAlign=Top", StringComparison.OrdinalIgnoreCase))
- {
- if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an7}";
- }
-
- if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an9}";
- }
-
- return "{\\an8}";
- }
-
- if (verticalAlign.Equals("$VertAlign=Center", StringComparison.OrdinalIgnoreCase))
- {
- if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an4}";
- }
-
- if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an6}";
- }
-
- return "{\\an5}";
- }
-
- if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an1}";
- }
-
- if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
- {
- return "{\\an3}";
- }
-
- return string.Empty;
- }
-
- internal static string DecodeStyles(string text)
- {
- var sb = new StringBuilder();
- bool italicOn = false;
- bool boldOn = false;
- bool skipNext = false;
- for (int i = 0; i < text.Length; i++)
- {
- if (skipNext)
- {
- skipNext = false;
- }
- else
- {
- if (text.Substring(i).StartsWith("^I", StringComparison.Ordinal))
- {
- sb.Append(!italicOn ? "" : "");
- italicOn = !italicOn;
- skipNext = true;
- }
- else if (text.Substring(i).StartsWith("^B", StringComparison.Ordinal))
- {
- sb.Append(!boldOn ? "" : "");
- boldOn = !boldOn;
- skipNext = true;
- }
- else
- {
- sb.Append(text[i]);
- }
- }
- }
- return sb.ToString();
- }
-
- internal static string EncodeStyles(string input)
- {
- var text = Utilities.RemoveSsaTags(input);
- text = text.Replace("", "").Replace("", "");
- bool allItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
- bool allBold = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
- bool allUnderline = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
- bool allUnderlineBoldItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
- bool allBoldItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1 && Utilities.CountTagInText(text, "") == 1;
-
- text = text.Replace("", "^I");
- text = text.Replace("", "^I");
- text = text.Replace("", "^I");
- text = text.Replace("", "^I");
-
- text = text.Replace("", "^B");
- text = text.Replace("", "^B");
- text = text.Replace("", "^B");
- text = text.Replace("", "^B");
-
- text = text.Replace("", "^U");
- text = text.Replace("", "^U");
- text = text.Replace("", "^U");
- text = text.Replace("", "^U");
-
- if (allUnderlineBoldItalic)
- {
- return text.Replace(Environment.NewLine, "^U^B^I|^I^B^U");
- }
-
- if (allBoldItalic)
- {
- return text.Replace(Environment.NewLine, "^U^B^I|^I^B^U");
- }
-
- if (allItalic)
- {
- return text.Replace(Environment.NewLine, "^I|^I");
- }
-
- if (allBold)
- {
- return text.Replace(Environment.NewLine, "^B|^B");
- }
-
- if (allUnderline)
- {
- return text.Replace(Environment.NewLine, "^U|^U");
- }
-
- return text.Replace(Environment.NewLine, "|");
- }
-
- internal static bool GetTimeCode(TimeCode timeCode, string timeString)
- {
- try
- {
- var timeParts = timeString.Split(':', ';');
- timeCode.Hours = int.Parse(timeParts[0]);
- timeCode.Minutes = int.Parse(timeParts[1]);
- timeCode.Seconds = int.Parse(timeParts[2]);
- timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3]));
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DvdStudioPro : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+[:;]\d+\t,\t\d+:\d+:\d+[:;]\d+\t,\t.*$", RegexOptions.Compiled);
+
+ public override string Extension => ".STL";
+
+ public override string Name => "DVD Studio Pro";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string paragraphWriteFormat = "{0}\t,\t{1}\t,\t{2}\r\n";
+ const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
+ var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
+
+ var lastVerticalAlign = "$VertAlign = Bottom";
+ var lastHorizontalcalAlign = "$HorzAlign = Center";
+ var sb = new StringBuilder();
+ sb.AppendLine(header);
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
+ string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
+ sb = ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
+ sb.AppendFormat(paragraphWriteFormat, startTime, endTime, EncodeStyles(p.Text));
+ }
+ return sb.ToString().Trim();
+ }
+
+ internal static StringBuilder ToTextAlignment(Paragraph p, StringBuilder sb, ref string lastVerticalAlign, ref string lastHorizontalAlign)
+ {
+ string verticalAlign;
+ string horizontalAlign;
+ bool verticalTopAlign = p.Text.StartsWith("{\\an7}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an8}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an9}", StringComparison.Ordinal);
+ bool verticalCenterAlign = p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an5}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an6}", StringComparison.Ordinal);
+ if (verticalTopAlign)
+ {
+ verticalAlign = "$VertAlign = Top";
+ }
+ else if (verticalCenterAlign)
+ {
+ verticalAlign = "$VertAlign = Center";
+ }
+ else
+ {
+ verticalAlign = "$VertAlign = Bottom";
+ }
+
+ if (lastVerticalAlign != verticalAlign)
+ {
+ sb.AppendLine(verticalAlign);
+ }
+
+ bool horizontalLeftAlign = p.Text.StartsWith("{\\an1}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an4}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an7}", StringComparison.Ordinal);
+ bool horizontalRightAlign = p.Text.StartsWith("{\\an3}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an6}", StringComparison.Ordinal) ||
+ p.Text.StartsWith("{\\an9}", StringComparison.Ordinal);
+ if (horizontalLeftAlign)
+ {
+ horizontalAlign = "$HorzAlign = Left";
+ }
+ else if (horizontalRightAlign)
+ {
+ horizontalAlign = "$HorzAlign = Right";
+ }
+ else
+ {
+ horizontalAlign = "$HorzAlign = Center";
+ }
+
+ if (lastHorizontalAlign != horizontalAlign)
+ {
+ sb.AppendLine(horizontalAlign);
+ }
+
+ lastVerticalAlign = verticalAlign;
+ lastHorizontalAlign = horizontalAlign;
+ return sb;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ int number = 0;
+ var verticalAlign = "$VertAlign=Bottom";
+ var horizontalAlign = "$HorzAlign=Center";
+ bool italicOn = false;
+ bool boldOn = false;
+ bool underlineOn = false;
+
+ foreach (string line in lines)
+ {
+ if (!string.IsNullOrWhiteSpace(line) && line[0] != '$')
+ {
+ if (RegexTimeCodes.Match(line).Success)
+ {
+ string[] threePart = line.Split(new[] { "\t,\t" }, StringSplitOptions.None);
+ var p = new Paragraph();
+ if (threePart.Length == 3 &&
+ GetTimeCode(p.StartTime, threePart[0]) &&
+ GetTimeCode(p.EndTime, threePart[1]))
+ {
+ number++;
+ p.Number = number;
+ p.Text = threePart[2].TrimEnd().Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
+ p.Text = DecodeStyles(p.Text);
+ if (italicOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (boldOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (underlineOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ p.Text = GetAlignment(verticalAlign, horizontalAlign) + p.Text;
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = false;
+ }
+ }
+ }
+
+ internal static string GetAlignment(string verticalAlign, string horizontalAlign)
+ {
+ if (verticalAlign.Equals("$VertAlign=Top", StringComparison.OrdinalIgnoreCase))
+ {
+ if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an7}";
+ }
+
+ if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an9}";
+ }
+
+ return "{\\an8}";
+ }
+
+ if (verticalAlign.Equals("$VertAlign=Center", StringComparison.OrdinalIgnoreCase))
+ {
+ if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an4}";
+ }
+
+ if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an6}";
+ }
+
+ return "{\\an5}";
+ }
+
+ if (horizontalAlign.Equals("$HorzAlign=Left", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an1}";
+ }
+
+ if (horizontalAlign.Equals("$HorzAlign=Right", StringComparison.OrdinalIgnoreCase))
+ {
+ return "{\\an3}";
+ }
+
+ return string.Empty;
+ }
+
+ internal static string DecodeStyles(string text)
+ {
+ var sb = new StringBuilder();
+ bool italicOn = false;
+ bool boldOn = false;
+ bool skipNext = false;
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (skipNext)
+ {
+ skipNext = false;
+ }
+ else
+ {
+ if (text.Substring(i).StartsWith("^I", StringComparison.Ordinal))
+ {
+ sb.Append(!italicOn ? "" : "");
+ italicOn = !italicOn;
+ skipNext = true;
+ }
+ else if (text.Substring(i).StartsWith("^B", StringComparison.Ordinal))
+ {
+ sb.Append(!boldOn ? "" : "");
+ boldOn = !boldOn;
+ skipNext = true;
+ }
+ else
+ {
+ sb.Append(text[i]);
+ }
+ }
+ }
+ return sb.ToString();
+ }
+
+ internal static string EncodeStyles(string input)
+ {
+ var text = Utilities.RemoveSsaTags(input);
+ text = text.Replace("", "").Replace("", "");
+ bool allItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
+ bool allBold = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
+ bool allUnderline = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
+ bool allUnderlineBoldItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1;
+ bool allBoldItalic = text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1 && Utilities.CountTagInText(text, "") == 1;
+
+ text = text.Replace("", "^I");
+ text = text.Replace("", "^I");
+ text = text.Replace("", "^I");
+ text = text.Replace("", "^I");
+
+ text = text.Replace("", "^B");
+ text = text.Replace("", "^B");
+ text = text.Replace("", "^B");
+ text = text.Replace("", "^B");
+
+ text = text.Replace("", "^U");
+ text = text.Replace("", "^U");
+ text = text.Replace("", "^U");
+ text = text.Replace("", "^U");
+
+ if (allUnderlineBoldItalic)
+ {
+ return text.Replace(Environment.NewLine, "^U^B^I|^I^B^U");
+ }
+
+ if (allBoldItalic)
+ {
+ return text.Replace(Environment.NewLine, "^U^B^I|^I^B^U");
+ }
+
+ if (allItalic)
+ {
+ return text.Replace(Environment.NewLine, "^I|^I");
+ }
+
+ if (allBold)
+ {
+ return text.Replace(Environment.NewLine, "^B|^B");
+ }
+
+ if (allUnderline)
+ {
+ return text.Replace(Environment.NewLine, "^U|^U");
+ }
+
+ return text.Replace(Environment.NewLine, "|");
+ }
+
+ internal static bool GetTimeCode(TimeCode timeCode, string timeString)
+ {
+ try
+ {
+ var timeParts = timeString.Split(':', ';');
+ timeCode.Hours = int.Parse(timeParts[0]);
+ timeCode.Minutes = int.Parse(timeParts[1]);
+ timeCode.Seconds = int.Parse(timeParts[2]);
+ timeCode.Milliseconds = FramesToMillisecondsMax999(int.Parse(timeParts[3]));
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DvdStudioProSpace.cs b/src/libse/SubtitleFormats/DvdStudioProSpace.cs
similarity index 97%
rename from libse/SubtitleFormats/DvdStudioProSpace.cs
rename to src/libse/SubtitleFormats/DvdStudioProSpace.cs
index 62404bc8b..8730d7002 100644
--- a/libse/SubtitleFormats/DvdStudioProSpace.cs
+++ b/src/libse/SubtitleFormats/DvdStudioProSpace.cs
@@ -1,126 +1,126 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DvdStudioProSpace : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+[:;]\d+ , \d+:\d+:\d+[:;]\d+ , .*$", RegexOptions.Compiled);
-
- public override string Extension => ".STL";
-
- public override string Name => "DVD Studio Pro with space";
-
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string paragraphWriteFormat = "{0} , {1} , {2}\r\n";
- const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
- var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
-
- var lastVerticalAlign = "$VertAlign = Bottom";
- var lastHorizontalcalAlign = "$HorzAlign = Center";
- var sb = new StringBuilder();
- sb.AppendLine(header);
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
- string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
- DvdStudioPro.ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
- sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text));
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- int number = 0;
- var verticalAlign = "$VertAlign=Bottom";
- var horizontalAlign = "$HorzAlign=Center";
- bool italicOn = false;
- bool boldOn = false;
- bool underlineOn = false;
- foreach (string line in lines)
- {
- if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//", StringComparison.Ordinal))
- {
- if (RegexTimeCodes.Match(line).Success)
- {
- string[] toPart = line.Substring(0, 25).Split(new[] { " ," }, StringSplitOptions.None);
- Paragraph p = new Paragraph();
- if (toPart.Length == 2 &&
- DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) &&
- DvdStudioPro.GetTimeCode(p.EndTime, toPart[1]))
- {
- number++;
- p.Number = number;
- string text = line.Substring(27).Trim();
- p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
- p.Text = DvdStudioPro.DecodeStyles(p.Text);
- if (p.Text.Trim().StartsWith("<>"))
- {
- _errorCount++;
- }
-
- if (italicOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (boldOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (underlineOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- p.Text = DvdStudioPro.GetAlignment(verticalAlign, horizontalAlign) + p.Text;
- subtitle.Paragraphs.Add(p);
- }
- }
- else
- {
- _errorCount++;
- }
- }
- else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
- {
- verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
- {
- horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = false;
- }
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DvdStudioProSpace : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+[:;]\d+ , \d+:\d+:\d+[:;]\d+ , .*$", RegexOptions.Compiled);
+
+ public override string Extension => ".STL";
+
+ public override string Name => "DVD Studio Pro with space";
+
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string paragraphWriteFormat = "{0} , {1} , {2}\r\n";
+ const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
+ var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
+
+ var lastVerticalAlign = "$VertAlign = Bottom";
+ var lastHorizontalcalAlign = "$HorzAlign = Center";
+ var sb = new StringBuilder();
+ sb.AppendLine(header);
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
+ string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
+ DvdStudioPro.ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
+ sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ int number = 0;
+ var verticalAlign = "$VertAlign=Bottom";
+ var horizontalAlign = "$HorzAlign=Center";
+ bool italicOn = false;
+ bool boldOn = false;
+ bool underlineOn = false;
+ foreach (string line in lines)
+ {
+ if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//", StringComparison.Ordinal))
+ {
+ if (RegexTimeCodes.Match(line).Success)
+ {
+ string[] toPart = line.Substring(0, 25).Split(new[] { " ," }, StringSplitOptions.None);
+ Paragraph p = new Paragraph();
+ if (toPart.Length == 2 &&
+ DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) &&
+ DvdStudioPro.GetTimeCode(p.EndTime, toPart[1]))
+ {
+ number++;
+ p.Number = number;
+ string text = line.Substring(27).Trim();
+ p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
+ p.Text = DvdStudioPro.DecodeStyles(p.Text);
+ if (p.Text.Trim().StartsWith("<>"))
+ {
+ _errorCount++;
+ }
+
+ if (italicOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (boldOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (underlineOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ p.Text = DvdStudioPro.GetAlignment(verticalAlign, horizontalAlign) + p.Text;
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = false;
+ }
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DvdStudioProSpaceGraphic.cs b/src/libse/SubtitleFormats/DvdStudioProSpaceGraphic.cs
similarity index 100%
rename from libse/SubtitleFormats/DvdStudioProSpaceGraphic.cs
rename to src/libse/SubtitleFormats/DvdStudioProSpaceGraphic.cs
diff --git a/libse/SubtitleFormats/DvdStudioProSpaceOne.cs b/src/libse/SubtitleFormats/DvdStudioProSpaceOne.cs
similarity index 97%
rename from libse/SubtitleFormats/DvdStudioProSpaceOne.cs
rename to src/libse/SubtitleFormats/DvdStudioProSpaceOne.cs
index 96082b4c7..42b17b1f7 100644
--- a/libse/SubtitleFormats/DvdStudioProSpaceOne.cs
+++ b/src/libse/SubtitleFormats/DvdStudioProSpaceOne.cs
@@ -1,125 +1,125 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DvdStudioProSpaceOne : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+,\d+:\d+:\d+:\d+, .*$", RegexOptions.Compiled);
-
- public override string Extension => ".STL";
-
- public override string Name => "DVD Studio Pro with one space";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string paragraphWriteFormat = "{0},{1}, {2}\r\n";
- const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
- var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
-
- var lastVerticalAlign = "$VertAlign = Bottom";
- var lastHorizontalcalAlign = "$HorzAlign = Center";
- var sb = new StringBuilder();
- sb.AppendLine(header);
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
- string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
- DvdStudioPro.ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
- sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text));
- }
- return sb.ToString().Trim();
- }
-
- public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate)
- {
- return (byte)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- int number = 0;
- var verticalAlign = "$VertAlign=Bottom";
- var horizontalAlign = "$HorzAlign=Center";
- bool italicOn = false;
- bool boldOn = false;
- bool underlineOn = false;
- foreach (string line in lines)
- {
- if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//", StringComparison.Ordinal))
- {
- if (RegexTimeCodes.Match(line).Success)
- {
- string[] toPart = line.Substring(0, 24).Trim(',').Split(',');
- var p = new Paragraph();
- if (toPart.Length == 2 &&
- DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) &&
- DvdStudioPro.GetTimeCode(p.EndTime, toPart[1]))
- {
- number++;
- p.Number = number;
- string text = line.Substring(25).Trim();
- p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
- p.Text = DvdStudioPro.DecodeStyles(p.Text);
- if (italicOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (boldOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- if (underlineOn && !p.Text.Contains(""))
- {
- p.Text = "" + p.Text + "";
- }
- p.Text = DvdStudioPro.GetAlignment(verticalAlign, horizontalAlign) + p.Text;
- subtitle.Paragraphs.Add(p);
- }
- }
- else
- {
- _errorCount++;
- }
- }
- else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
- {
- verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
- {
- horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
- }
- else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
- {
- italicOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
- {
- boldOn = false;
- }
- else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = true;
- }
- else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
- {
- underlineOn = false;
- }
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DvdStudioProSpaceOne : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d+:\d+:\d+:\d+,\d+:\d+:\d+:\d+, .*$", RegexOptions.Compiled);
+
+ public override string Extension => ".STL";
+
+ public override string Name => "DVD Studio Pro with one space";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string paragraphWriteFormat = "{0},{1}, {2}\r\n";
+ const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
+ var header = Configuration.Settings.SubtitleSettings.DvdStudioProHeader.TrimEnd() + Environment.NewLine;
+
+ var lastVerticalAlign = "$VertAlign = Bottom";
+ var lastHorizontalcalAlign = "$HorzAlign = Center";
+ var sb = new StringBuilder();
+ sb.AppendLine(header);
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ string startTime = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, MillisecondsToFramesMaxFrameRate(p.StartTime.Milliseconds));
+ string endTime = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, MillisecondsToFramesMaxFrameRate(p.EndTime.Milliseconds));
+ DvdStudioPro.ToTextAlignment(p, sb, ref lastVerticalAlign, ref lastHorizontalcalAlign);
+ sb.AppendFormat(paragraphWriteFormat, startTime, endTime, DvdStudioPro.EncodeStyles(p.Text));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate)
+ {
+ return (byte)Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ int number = 0;
+ var verticalAlign = "$VertAlign=Bottom";
+ var horizontalAlign = "$HorzAlign=Center";
+ bool italicOn = false;
+ bool boldOn = false;
+ bool underlineOn = false;
+ foreach (string line in lines)
+ {
+ if (!string.IsNullOrWhiteSpace(line) && line[0] != '$' && !line.StartsWith("//", StringComparison.Ordinal))
+ {
+ if (RegexTimeCodes.Match(line).Success)
+ {
+ string[] toPart = line.Substring(0, 24).Trim(',').Split(',');
+ var p = new Paragraph();
+ if (toPart.Length == 2 &&
+ DvdStudioPro.GetTimeCode(p.StartTime, toPart[0]) &&
+ DvdStudioPro.GetTimeCode(p.EndTime, toPart[1]))
+ {
+ number++;
+ p.Number = number;
+ string text = line.Substring(25).Trim();
+ p.Text = text.Replace(" | ", Environment.NewLine).Replace("|", Environment.NewLine);
+ p.Text = DvdStudioPro.DecodeStyles(p.Text);
+ if (italicOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (boldOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ if (underlineOn && !p.Text.Contains(""))
+ {
+ p.Text = "" + p.Text + "";
+ }
+ p.Text = DvdStudioPro.GetAlignment(verticalAlign, horizontalAlign) + p.Text;
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ else
+ {
+ _errorCount++;
+ }
+ }
+ else if (line != null && line.TrimStart().StartsWith("$VertAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ verticalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line != null && line.TrimStart().StartsWith("$HorzAlign", StringComparison.OrdinalIgnoreCase))
+ {
+ horizontalAlign = line.RemoveChar(' ').RemoveChar('\t');
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Italic=True", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Italic=False", StringComparison.OrdinalIgnoreCase))
+ {
+ italicOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Bold=True", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Bold=False", StringComparison.OrdinalIgnoreCase))
+ {
+ boldOn = false;
+ }
+ else if (line.Replace(" ", string.Empty).Equals("$Underlined=True", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = true;
+ }
+ else if (line.Replace(" ", string.Empty).Trim().Equals("$Underlined=False", StringComparison.OrdinalIgnoreCase))
+ {
+ underlineOn = false;
+ }
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/DvdStudioProSpaceOneSemicolon.cs b/src/libse/SubtitleFormats/DvdStudioProSpaceOneSemicolon.cs
similarity index 100%
rename from libse/SubtitleFormats/DvdStudioProSpaceOneSemicolon.cs
rename to src/libse/SubtitleFormats/DvdStudioProSpaceOneSemicolon.cs
diff --git a/libse/SubtitleFormats/DvdSubtitle.cs b/src/libse/SubtitleFormats/DvdSubtitle.cs
similarity index 97%
rename from libse/SubtitleFormats/DvdSubtitle.cs
rename to src/libse/SubtitleFormats/DvdSubtitle.cs
index 9d3f6bece..eb7580029 100644
--- a/libse/SubtitleFormats/DvdSubtitle.cs
+++ b/src/libse/SubtitleFormats/DvdSubtitle.cs
@@ -1,139 +1,139 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DvdSubtitle : SubtitleFormat
- {
-
- private static readonly Regex RegexTimeCodes = new Regex(@"^\{T \d+:\d+:\d+:\d+$", RegexOptions.Compiled);
-
- public override string Extension => ".sub";
-
- public override string Name => "DVDSubtitle";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- const string paragraphWriteFormat = "T {0}\r\n{1}\r\n";
- const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
- const string header = @"{HEAD
-DISCID=
-DVDTITLE=
-CODEPAGE=1250
-FORMAT=ASCII
-LANG=
-TITLE=1
-ORIGINAL=ORIGINAL
-AUTHOR=
-WEB=
-INFO=
-LICENSE=
-}";
-
- var sb = new StringBuilder();
- sb.AppendLine(header);
- foreach (var p in subtitle.Paragraphs)
- {
- int milliseconds = p.StartTime.Milliseconds / 10;
- string time = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, milliseconds);
- sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, p.Text) + "}");
-
- milliseconds = p.EndTime.Milliseconds / 10;
- time = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, milliseconds);
- sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, string.Empty) + "}");
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- //{T 00:03:14:27
- //Some text
- //}
- _errorCount = 0;
- bool textOn = false;
- string text = string.Empty;
- var start = new TimeCode();
- var end = new TimeCode();
- foreach (string line in lines)
- {
- if (textOn)
- {
- if (line.Trim() == "}")
- {
- var p = new Paragraph
- {
- Text = text,
- StartTime = new TimeCode(start.TotalMilliseconds),
- EndTime = new TimeCode(end.TotalMilliseconds)
- };
-
- subtitle.Paragraphs.Add(p);
-
- text = string.Empty;
- start = new TimeCode();
- end = new TimeCode();
- textOn = false;
- }
- else
- {
- if (text.Length == 0)
- {
- text = line;
- }
- else
- {
- text += Environment.NewLine + line;
- }
- }
- }
- else
- {
- if (RegexTimeCodes.Match(line).Success)
- {
- try
- {
- textOn = true;
- string[] arr = line.Substring(3).Trim().Split(':');
- if (arr.Length == 4)
- {
- int hours = int.Parse(arr[0]);
- int minutes = int.Parse(arr[1]);
- int seconds = int.Parse(arr[2]);
- int milliseconds = int.Parse(arr[3]);
- if (arr[3].Length == 2)
- {
- milliseconds *= 10;
- }
-
- start = new TimeCode(hours, minutes, seconds, milliseconds);
- }
- }
- catch
- {
- textOn = false;
- _errorCount++;
- }
- }
- }
- }
-
- int index = 1;
- foreach (var p in subtitle.Paragraphs)
- {
- var next = subtitle.GetParagraphOrDefault(index);
- if (next != null)
- {
- p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds;
- }
- index++;
- }
-
- subtitle.RemoveEmptyLines();
- subtitle.Renumber();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DvdSubtitle : SubtitleFormat
+ {
+
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\{T \d+:\d+:\d+:\d+$", RegexOptions.Compiled);
+
+ public override string Extension => ".sub";
+
+ public override string Name => "DVDSubtitle";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ const string paragraphWriteFormat = "T {0}\r\n{1}\r\n";
+ const string timeFormat = "{0:00}:{1:00}:{2:00}:{3:00}";
+ const string header = @"{HEAD
+DISCID=
+DVDTITLE=
+CODEPAGE=1250
+FORMAT=ASCII
+LANG=
+TITLE=1
+ORIGINAL=ORIGINAL
+AUTHOR=
+WEB=
+INFO=
+LICENSE=
+}";
+
+ var sb = new StringBuilder();
+ sb.AppendLine(header);
+ foreach (var p in subtitle.Paragraphs)
+ {
+ int milliseconds = p.StartTime.Milliseconds / 10;
+ string time = string.Format(timeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, milliseconds);
+ sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, p.Text) + "}");
+
+ milliseconds = p.EndTime.Milliseconds / 10;
+ time = string.Format(timeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, milliseconds);
+ sb.AppendLine("{" + string.Format(paragraphWriteFormat, time, string.Empty) + "}");
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ //{T 00:03:14:27
+ //Some text
+ //}
+ _errorCount = 0;
+ bool textOn = false;
+ string text = string.Empty;
+ var start = new TimeCode();
+ var end = new TimeCode();
+ foreach (string line in lines)
+ {
+ if (textOn)
+ {
+ if (line.Trim() == "}")
+ {
+ var p = new Paragraph
+ {
+ Text = text,
+ StartTime = new TimeCode(start.TotalMilliseconds),
+ EndTime = new TimeCode(end.TotalMilliseconds)
+ };
+
+ subtitle.Paragraphs.Add(p);
+
+ text = string.Empty;
+ start = new TimeCode();
+ end = new TimeCode();
+ textOn = false;
+ }
+ else
+ {
+ if (text.Length == 0)
+ {
+ text = line;
+ }
+ else
+ {
+ text += Environment.NewLine + line;
+ }
+ }
+ }
+ else
+ {
+ if (RegexTimeCodes.Match(line).Success)
+ {
+ try
+ {
+ textOn = true;
+ string[] arr = line.Substring(3).Trim().Split(':');
+ if (arr.Length == 4)
+ {
+ int hours = int.Parse(arr[0]);
+ int minutes = int.Parse(arr[1]);
+ int seconds = int.Parse(arr[2]);
+ int milliseconds = int.Parse(arr[3]);
+ if (arr[3].Length == 2)
+ {
+ milliseconds *= 10;
+ }
+
+ start = new TimeCode(hours, minutes, seconds, milliseconds);
+ }
+ }
+ catch
+ {
+ textOn = false;
+ _errorCount++;
+ }
+ }
+ }
+ }
+
+ int index = 1;
+ foreach (var p in subtitle.Paragraphs)
+ {
+ var next = subtitle.GetParagraphOrDefault(index);
+ if (next != null)
+ {
+ p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds;
+ }
+ index++;
+ }
+
+ subtitle.RemoveEmptyLines();
+ subtitle.Renumber();
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/DvdSubtitleSystem.cs b/src/libse/SubtitleFormats/DvdSubtitleSystem.cs
similarity index 97%
rename from libse/SubtitleFormats/DvdSubtitleSystem.cs
rename to src/libse/SubtitleFormats/DvdSubtitleSystem.cs
index 1230edf9b..bb67209d0 100644
--- a/libse/SubtitleFormats/DvdSubtitleSystem.cs
+++ b/src/libse/SubtitleFormats/DvdSubtitleSystem.cs
@@ -1,87 +1,87 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class DvdSubtitleSystem : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d ", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "DVD Subtitle System";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- foreach (string line in lines)
- {
- sb.AppendLine(line);
- }
-
- if (sb.ToString().Contains("#INPOINT OUTPOINT PATH"))
- {
- return false; // Pinnacle Impression
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var sb = new StringBuilder();
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- //00:03:15:22 00:03:23:10 This is line one.
- //This is line two.
- sb.AppendLine($"{EncodeTimeCode(p.StartTime)} {EncodeTimeCode(p.EndTime)} {HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "//"), true)}");
- }
- return sb.ToString();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- //00:03:15:22 (last is ms div 10)
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- //00:03:15:22 00:03:23:10 This is line one.
- //This is line two.
- subtitle.Paragraphs.Clear();
- _errorCount = 0;
- foreach (string line in lines)
- {
- // line must contain atleast 24 characters (time-code)...
- if (line.Length < 24)
- {
- _errorCount += 10;
- continue;
- }
-
- Match match = RegexTimeCodes.Match(line);
-
- if (match.Success)
- {
- string temp = line.Substring(0, match.Length);
- string start = temp.Substring(0, 11);
- string end = temp.Substring(12, 11);
-
- string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- string text = line.Substring(match.Length).Trim();
- text = text.Replace("//", Environment.NewLine);
- var p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), text);
- subtitle.Paragraphs.Add(p);
- }
- }
-
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class DvdSubtitleSystem : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d ", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "DVD Subtitle System";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ foreach (string line in lines)
+ {
+ sb.AppendLine(line);
+ }
+
+ if (sb.ToString().Contains("#INPOINT OUTPOINT PATH"))
+ {
+ return false; // Pinnacle Impression
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var sb = new StringBuilder();
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ //00:03:15:22 00:03:23:10 This is line one.
+ //This is line two.
+ sb.AppendLine($"{EncodeTimeCode(p.StartTime)} {EncodeTimeCode(p.EndTime)} {HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, "//"), true)}");
+ }
+ return sb.ToString();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ //00:03:15:22 (last is ms div 10)
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ //00:03:15:22 00:03:23:10 This is line one.
+ //This is line two.
+ subtitle.Paragraphs.Clear();
+ _errorCount = 0;
+ foreach (string line in lines)
+ {
+ // line must contain atleast 24 characters (time-code)...
+ if (line.Length < 24)
+ {
+ _errorCount += 10;
+ continue;
+ }
+
+ Match match = RegexTimeCodes.Match(line);
+
+ if (match.Success)
+ {
+ string temp = line.Substring(0, match.Length);
+ string start = temp.Substring(0, 11);
+ string end = temp.Substring(12, 11);
+
+ string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ string text = line.Substring(match.Length).Trim();
+ text = text.Replace("//", Environment.NewLine);
+ var p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), text);
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/ELRStudioClosedCaption.cs b/src/libse/SubtitleFormats/ELRStudioClosedCaption.cs
similarity index 97%
rename from libse/SubtitleFormats/ELRStudioClosedCaption.cs
rename to src/libse/SubtitleFormats/ELRStudioClosedCaption.cs
index f355cb2a4..ef1a7faf6 100644
--- a/libse/SubtitleFormats/ELRStudioClosedCaption.cs
+++ b/src/libse/SubtitleFormats/ELRStudioClosedCaption.cs
@@ -1,144 +1,144 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class ELRStudioClosedCaption : SubtitleFormat
- {
- public override string Extension => ".elr";
-
- public override string Name => "ELRStudio Closed Caption";
-
- public static void Save(string fileName)
- {
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- //...
- }
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big
- {
- if (fileName.EndsWith(".elr", StringComparison.OrdinalIgnoreCase))
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- byte[] compareBuffer = { 0x05, 0x01, 0x0D, 0x15, 0x11, 0x00, 0xA9, 0x00, 0x45, 0x00, 0x6C, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6F, 0x00 };
-
- for (int i = 6; i < compareBuffer.Length; i++)
- {
- if (buffer[i] != compareBuffer[i])
- {
- return false;
- }
- }
-
- var sub = new Subtitle();
- LoadSubtitle(sub, lines, fileName);
- return sub.Paragraphs.Count > 0;
- }
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
-
- int i = 128;
- while (i < buffer.Length - 40)
- {
- try
- {
- if ((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10) // start time (hopefully)
- {
- var p = new Paragraph { StartTime = GetTimeCode(buffer, i + 4) };
- i += 7;
-
- // seek to endtime
- while (i < buffer.Length - 10 && !((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10))
- {
- i++;
- }
- if (buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10)
- {
- p.EndTime = GetTimeCode(buffer, i + 4);
- i += 7;
- }
- if (Math.Abs(p.EndTime.TotalMilliseconds) < 0.001)
- {
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 2000;
- }
-
- // seek to text
- var sb = new StringBuilder();
- int min = 4;
- while (min > 0 || i < buffer.Length - 10 && !((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10))
- {
- min--;
- if (buffer[i] == 9 && buffer[i + 1] == 0 && buffer[i + 2] == 0x44)
- {
- var length = buffer[i - 1];
- i += 12;
- for (int j = i; j < i + length * 4; j += 4)
- {
- sb.Append(Encoding.GetEncoding(1252).GetString(buffer, j, 1));
- }
- sb.AppendLine();
- }
- else
- {
- i++;
- }
- }
- p.Text = (p.Text + " " + sb).Trim();
- subtitle.Paragraphs.Add(p);
- }
- else
- {
- i++;
- }
- }
- catch
- {
- i += 5;
- }
- }
- subtitle.Renumber();
- }
-
- private static TimeCode GetTimeCode(byte[] buffer, int idx)
- {
- try
- {
- const string format = "X4";
- int frames = int.Parse(buffer[idx].ToString(format));
- int seconds = int.Parse(buffer[idx + 1].ToString(format));
- int minutes = int.Parse(buffer[idx + 2].ToString(format));
- int hours = int.Parse(buffer[idx + 3].ToString(format));
- return new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames));
- }
- catch
- {
- return new TimeCode();
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class ELRStudioClosedCaption : SubtitleFormat
+ {
+ public override string Extension => ".elr";
+
+ public override string Name => "ELRStudio Closed Caption";
+
+ public static void Save(string fileName)
+ {
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ //...
+ }
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 640 && fi.Length < 1024000) // not too small or too big
+ {
+ if (fileName.EndsWith(".elr", StringComparison.OrdinalIgnoreCase))
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ byte[] compareBuffer = { 0x05, 0x01, 0x0D, 0x15, 0x11, 0x00, 0xA9, 0x00, 0x45, 0x00, 0x6C, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6F, 0x00 };
+
+ for (int i = 6; i < compareBuffer.Length; i++)
+ {
+ if (buffer[i] != compareBuffer[i])
+ {
+ return false;
+ }
+ }
+
+ var sub = new Subtitle();
+ LoadSubtitle(sub, lines, fileName);
+ return sub.Paragraphs.Count > 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+
+ int i = 128;
+ while (i < buffer.Length - 40)
+ {
+ try
+ {
+ if ((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10) // start time (hopefully)
+ {
+ var p = new Paragraph { StartTime = GetTimeCode(buffer, i + 4) };
+ i += 7;
+
+ // seek to endtime
+ while (i < buffer.Length - 10 && !((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10))
+ {
+ i++;
+ }
+ if (buffer[i] == 0xc4 && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10)
+ {
+ p.EndTime = GetTimeCode(buffer, i + 4);
+ i += 7;
+ }
+ if (Math.Abs(p.EndTime.TotalMilliseconds) < 0.001)
+ {
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 2000;
+ }
+
+ // seek to text
+ var sb = new StringBuilder();
+ int min = 4;
+ while (min > 0 || i < buffer.Length - 10 && !((buffer[i] == 0xc4 || buffer[i] == 0x5d) && buffer[i + 1] == 9 && buffer[i + 2] == 0 && buffer[i + 3] == 0x10))
+ {
+ min--;
+ if (buffer[i] == 9 && buffer[i + 1] == 0 && buffer[i + 2] == 0x44)
+ {
+ var length = buffer[i - 1];
+ i += 12;
+ for (int j = i; j < i + length * 4; j += 4)
+ {
+ sb.Append(Encoding.GetEncoding(1252).GetString(buffer, j, 1));
+ }
+ sb.AppendLine();
+ }
+ else
+ {
+ i++;
+ }
+ }
+ p.Text = (p.Text + " " + sb).Trim();
+ subtitle.Paragraphs.Add(p);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ catch
+ {
+ i += 5;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static TimeCode GetTimeCode(byte[] buffer, int idx)
+ {
+ try
+ {
+ const string format = "X4";
+ int frames = int.Parse(buffer[idx].ToString(format));
+ int seconds = int.Parse(buffer[idx + 1].ToString(format));
+ int minutes = int.Parse(buffer[idx + 2].ToString(format));
+ int hours = int.Parse(buffer[idx + 3].ToString(format));
+ return new TimeCode(hours, minutes, seconds, FramesToMillisecondsMax999(frames));
+ }
+ catch
+ {
+ return new TimeCode();
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/ESubXf.cs b/src/libse/SubtitleFormats/ESubXf.cs
similarity index 100%
rename from libse/SubtitleFormats/ESubXf.cs
rename to src/libse/SubtitleFormats/ESubXf.cs
diff --git a/libse/SubtitleFormats/EZTSubtitlesProject.cs b/src/libse/SubtitleFormats/EZTSubtitlesProject.cs
similarity index 100%
rename from libse/SubtitleFormats/EZTSubtitlesProject.cs
rename to src/libse/SubtitleFormats/EZTSubtitlesProject.cs
diff --git a/libse/SubtitleFormats/Ebu.cs b/src/libse/SubtitleFormats/Ebu.cs
similarity index 97%
rename from libse/SubtitleFormats/Ebu.cs
rename to src/libse/SubtitleFormats/Ebu.cs
index 4c04a40fd..9c827671d 100644
--- a/libse/SubtitleFormats/Ebu.cs
+++ b/src/libse/SubtitleFormats/Ebu.cs
@@ -1,1785 +1,1785 @@
-using Nikse.SubtitleEdit.Core.Interfaces;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- ///
- /// EBU Subtitling data exchange format
- ///
- public class Ebu : SubtitleFormat, IBinaryPersistableSubtitle
- {
- private static readonly Regex FontTagsNoSpace1 = new Regex("[a-zA-z.!?][a-zA-Z-]", RegexOptions.Compiled);
- private static readonly Regex FontTagsNoSpace2 = new Regex("[a-zA-z.!?][a-zA-Z-]", RegexOptions.Compiled);
-
- private static readonly Regex FontTagsStartSpace = new Regex("^ ", RegexOptions.Compiled); // " "
- private static readonly Regex FontTagsNewLineSpace = new Regex("[\r\n]+ ", RegexOptions.Compiled); // "\r\n "
-
- private const string LanguageCodeChinese = "75";
-
- public interface IEbuUiHelper
- {
- void Initialize(EbuGeneralSubtitleInformation header, byte justificationCode, string fileName, Subtitle subtitle);
- bool ShowDialogOk();
- byte JustificationCode { get; set; }
- }
-
- public static IEbuUiHelper EbuUiHelper { get; set; }
-
- private static readonly Regex RegExprColor = new Regex(@"^[a-f0-9]{6}$", RegexOptions.Compiled);
-
- public List VerticalPositions = new List();
- public List JustificationCodes = new List();
-
- public EbuGeneralSubtitleInformation Header;
-
- ///
- /// GSI block (1024 bytes)
- ///
- public class EbuGeneralSubtitleInformation
- {
- public string CodePageNumber { get; set; } // 0..2
- public string DiskFormatCode { get; set; } // 3..10
- public double FrameRateFromSaveDialog { get; set; }
- public string DisplayStandardCode { get; set; } // 11
- public string CharacterCodeTableNumber { get; set; } // 12..13
- public string LanguageCode { get; set; } // 14..15
- public string OriginalProgrammeTitle { get; set; } // 16..47
- public string OriginalEpisodeTitle { get; set; }
- public string TranslatedProgrammeTitle { get; set; }
- public string TranslatedEpisodeTitle { get; set; }
- public string TranslatorsName { get; set; }
- public string TranslatorsContactDetails { get; set; }
- public string SubtitleListReferenceCode { get; set; }
- public string CreationDate { get; set; }
- public string RevisionDate { get; set; }
- public string RevisionNumber { get; set; }
- public string TotalNumberOfTextAndTimingInformationBlocks { get; set; }
- public string TotalNumberOfSubtitles { get; set; }
- public string TotalNumberOfSubtitleGroups { get; set; }
- public string MaximumNumberOfDisplayableCharactersInAnyTextRow { get; set; }
- public string MaximumNumberOfDisplayableRows { get; set; }
- public string TimeCodeStatus { get; set; }
- public string TimeCodeStartOfProgramme { get; set; }
- public string TimeCodeFirstInCue { get; set; }
- public string TotalNumberOfDisks { get; set; }
- public string DiskSequenceNumber { get; set; }
- public string CountryOfOrigin { get; set; }
- public string Publisher { get; set; }
- public string EditorsName { get; set; }
- public string EditorsContactDetails { get; set; }
- public string SpareBytes { get; set; }
- public string UserDefinedArea { get; set; }
-
- public double FrameRate
- {
- get
- {
- if (FrameRateFromSaveDialog > 20)
- {
- return FrameRateFromSaveDialog;
- }
-
- if (DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal))
- {
- return 23.0;
- }
-
- if (DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal))
- {
- return 24.0;
- }
-
- if (DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal))
- {
- return 25.0;
- }
-
- if (DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal))
- {
- return 29.0;
- }
-
- if (DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal))
- {
- return 35.0;
- }
-
- if (DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal))
- {
- return 48.0;
- }
-
- if (DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal))
- {
- return 50.0;
- }
-
- if (DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal))
- {
- return 60.0;
- }
-
- return 30.0; // should be DiskFormatcode STL30.01
- }
- }
-
- public EbuGeneralSubtitleInformation()
- {
- CodePageNumber = "437";
- DiskFormatCode = "STL25.01";
- DisplayStandardCode = "0"; // 0=Open subtitling
- CharacterCodeTableNumber = "00";
- LanguageCode = "0A";
- OriginalProgrammeTitle = "No Title ";
- OriginalEpisodeTitle = " ";
- TranslatedProgrammeTitle = string.Empty.PadLeft(32, ' ');
- TranslatedEpisodeTitle = string.Empty.PadLeft(32, ' ');
- TranslatorsName = string.Empty.PadLeft(32, ' ');
- TranslatorsContactDetails = string.Empty.PadLeft(32, ' ');
- SubtitleListReferenceCode = "0 ";
- CreationDate = "101021";
- RevisionDate = "101021";
- RevisionNumber = "01";
- TotalNumberOfTextAndTimingInformationBlocks = "00725";
- TotalNumberOfSubtitles = "00725";
- TotalNumberOfSubtitleGroups = "001";
- MaximumNumberOfDisplayableCharactersInAnyTextRow = "40";
- MaximumNumberOfDisplayableRows = "23";
- TimeCodeStatus = "1";
- TimeCodeStartOfProgramme = "00000000";
- TimeCodeFirstInCue = "00000001";
- TotalNumberOfDisks = "1";
- DiskSequenceNumber = "1";
- CountryOfOrigin = "USA";
- Publisher = string.Empty.PadLeft(32, ' ');
- EditorsName = string.Empty.PadLeft(32, ' ');
- EditorsContactDetails = string.Empty.PadLeft(32, ' ');
- SpareBytes = string.Empty.PadLeft(75, ' ');
- UserDefinedArea = string.Empty.PadLeft(576, ' ');
- }
-
- public override string ToString()
- {
- var result = CodePageNumber +
- DiskFormatCode +
- DisplayStandardCode +
- CharacterCodeTableNumber +
- LanguageCode +
- OriginalProgrammeTitle +
- OriginalEpisodeTitle +
- TranslatedProgrammeTitle +
- TranslatedEpisodeTitle +
- TranslatorsName +
- TranslatorsContactDetails +
- SubtitleListReferenceCode +
- CreationDate +
- RevisionDate +
- RevisionNumber +
- TotalNumberOfTextAndTimingInformationBlocks +
- TotalNumberOfSubtitles +
- TotalNumberOfSubtitleGroups +
- MaximumNumberOfDisplayableCharactersInAnyTextRow +
- MaximumNumberOfDisplayableRows +
- TimeCodeStatus +
- TimeCodeStartOfProgramme +
- TimeCodeFirstInCue +
- TotalNumberOfDisks +
- DiskSequenceNumber +
- CountryOfOrigin +
- Publisher +
- EditorsName +
- EditorsContactDetails +
- SpareBytes +
- UserDefinedArea;
-
- if (result.Length == 1024)
- {
- return result;
- }
-
- return "Length must be 1024 but is " + result.Length;
- }
- }
-
- ///
- /// TTI block 128 bytes
- ///
- private class EbuTextTimingInformation
- {
- public byte SubtitleGroupNumber { get; set; }
- public ushort SubtitleNumber { get; set; }
- public byte ExtensionBlockNumber { get; set; }
- public byte CumulativeStatus { get; set; }
- public int TimeCodeInHours { get; set; }
- public int TimeCodeInMinutes { get; set; }
- public int TimeCodeInSeconds { get; set; }
- public int TimeCodeInMilliseconds { get; set; }
- public int TimeCodeOutHours { get; set; }
- public int TimeCodeOutMinutes { get; set; }
- public int TimeCodeOutSeconds { get; set; }
- public int TimeCodeOutMilliseconds { get; set; }
- public byte VerticalPosition { get; set; }
- public byte JustificationCode { get; set; }
- public byte CommentFlag { get; set; }
- public string TextField { get; set; }
-
- public EbuTextTimingInformation()
- {
- SubtitleGroupNumber = 0;
- ExtensionBlockNumber = 255;
- CumulativeStatus = 0;
- VerticalPosition = 0x16;
- JustificationCode = 2;
- CommentFlag = 0;
- }
-
- public byte[] GetBytes(EbuGeneralSubtitleInformation header)
- {
- var buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes
-
- buffer[0] = SubtitleGroupNumber;
- var temp = BitConverter.GetBytes(SubtitleNumber);
- buffer[1] = temp[0];
- buffer[2] = temp[1];
- buffer[3] = ExtensionBlockNumber;
- buffer[4] = CumulativeStatus;
-
- buffer[5] = (byte)TimeCodeInHours;
- buffer[6] = (byte)TimeCodeInMinutes;
- var frames = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate, out var extraSeconds);
- buffer[7] = (byte)(TimeCodeInSeconds + extraSeconds);
- buffer[8] = frames;
-
- buffer[9] = (byte)TimeCodeOutHours;
- buffer[10] = (byte)TimeCodeOutMinutes;
- frames = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate, out extraSeconds);
- buffer[11] = (byte)(TimeCodeOutSeconds + extraSeconds);
- buffer[12] = frames;
-
- buffer[13] = VerticalPosition;
- buffer[14] = JustificationCode;
- buffer[15] = CommentFlag;
-
- var encoding = GetEncoding(header.CodePageNumber);
- if (header.LanguageCode == LanguageCodeChinese)
- {
- var lines = HtmlUtil.RemoveHtmlTags(TextField, true).SplitToLines();
- var byteList = new List();
- encoding = Encoding.GetEncoding(1200); // 16-bit Unicode
- for (var i = 0; i < lines.Count; i++)
- {
- var l = lines[i];
- if (i > 0)
- { // new line
- byteList.Add(0);
- byteList.Add(138);
- }
- byteList.AddRange(encoding.GetBytes(l).ToArray());
- }
-
- for (var i = 0; i < 112; i++)
- {
- if (i < byteList.Count)
- {
- buffer[16 + i] = byteList[i];
- }
- else
- {
- buffer[16 + i] = 0x8f;
- }
- }
-
- return buffer;
- }
-
- if (header.CharacterCodeTableNumber == "00")
- {
- encoding = Encoding.GetEncoding(20269);
- // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937
-
- var sbTwoChar = new StringBuilder();
- bool skipNext = false;
- for (var index = 0; index < TextField.Length; index++)
- {
- var ch = TextField[index];
- if (skipNext)
- {
- skipNext = false;
- }
- else if (ch == 'ı' && TextField.Substring(index).StartsWith("ı̂")) // extended unicode char - rewritten as simple 'î' - looks the same as "î" but it's not...)
- {
- sbTwoChar.Append(encoding.GetString(new byte[] { 0xc3, 0x69 })); // Ãi - simple î
- skipNext = true;
- }
- else if ("ÀÈÌÒÙàèìòù".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"));
- }
- else if ("ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"));
- }
- else if ("ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥĵôŝûŵŷîı̂".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"));
- }
- else if ("ÃĨÑÕŨãĩñõũ".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"));
- }
- else if ("ĀĒĪŌŪāēīōū".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"));
- }
- else if ("ĂĞŬăğŭ".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"));
- }
- else if ("ĊĖĠİŻċėġıż".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"));
- }
- else if ("ÄËÏÖÜŸäëïöüÿ".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"));
- }
- else if ("ÅŮåů".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"));
- }
- else if ("ÇĢĶĻŅŖŞŢçķļņŗşţ".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"));
- }
- else if ("ŐŰőű".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"));
- }
- else if ("ĄĘĮŲąęįų".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"));
- }
- else if ("ČĎĚĽŇŘŠŤŽčďěľňřšťž".Contains(ch))
- {
- sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"));
- }
- else
- {
- sbTwoChar.Append(ch);
- }
- }
-
- TextField = sbTwoChar.ToString();
- }
- else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988
- {
- encoding = Encoding.GetEncoding("ISO-8859-5");
- }
- else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987
- {
- encoding = Encoding.GetEncoding("ISO-8859-6");
- }
- else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987
- {
- encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ?
- }
- else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988
- {
- encoding = Encoding.GetEncoding("ISO-8859-8");
- }
-
- // italic/underline
- var italicsOn = encoding.GetString(new byte[] { 0x80 });
- var italicsOff = encoding.GetString(new byte[] { 0x81 });
- var underlineOn = encoding.GetString(new byte[] { 0x82 });
- var underlineOff = encoding.GetString(new byte[] { 0x83 });
- var boxingOn = encoding.GetString(new byte[] { 0x84 });
- var boxingOff = encoding.GetString(new byte[] { 0x85 });
-
- TextField = FixItalics(TextField);
-
- TextField = TextField.Replace("", italicsOn);
- TextField = TextField.Replace("", italicsOn);
- TextField = TextField.Replace("", italicsOff);
- TextField = TextField.Replace("", italicsOff);
- TextField = TextField.Replace("", underlineOn);
- TextField = TextField.Replace("", underlineOn);
- TextField = TextField.Replace("", underlineOff);
- TextField = TextField.Replace("", underlineOff);
- TextField = TextField.Replace("", boxingOn);
- TextField = TextField.Replace("", boxingOn);
- TextField = TextField.Replace("", boxingOff);
- TextField = TextField.Replace("", boxingOff);
- if (header.CharacterCodeTableNumber == "00")
- {
- TextField = TextField.Replace("©", encoding.GetString(new byte[] { 0xd3 }));
- TextField = TextField.Replace("™", encoding.GetString(new byte[] { 0xd4 }));
- TextField = TextField.Replace("♪", encoding.GetString(new byte[] { 0xd5 }));
- }
-
- TextField = EncodeText(TextField, encoding, header.DisplayStandardCode);
- TextField = HtmlUtil.RemoveHtmlTags(TextField, true);
-
- if (header.DisplayStandardCode != "0") // 0=Open subtitling
- {
- if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
- {
- TextField = encoding.GetString(new byte[] { 0x0d, 0x0b, 0x0b }) + TextField; // d=double height, b=start box
- }
- else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox)
- {
- TextField = encoding.GetString(new byte[] { 0x0b, 0x0b }) + TextField; // b=start box
- }
- else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
- {
- TextField = encoding.GetString(new byte[] { 0x0d }) + TextField; // d=double height
- }
- }
-
- // convert text to bytes
- var bytes = encoding.GetBytes(TextField);
-
- // some fixes for bytes
- if (bytes.Length == TextField.Length)
- {
- for (var i = 0; i < bytes.Length; i++)
- {
- if (TextField[i] == '#')
- {
- bytes[i] = 0x23;
- }
- else if (TextField[i] == 'Đ')
- {
- bytes[i] = 0xe2;
- }
- else if (TextField[i] == '–') // em dash
- {
- bytes[i] = 0xd0;
- }
- }
- }
-
- for (var i = 0; i < 112; i++)
- {
- if (i < bytes.Length)
- {
- buffer[16 + i] = bytes[i];
- }
- else
- {
- buffer[16 + i] = 0x8f;
- }
- }
- return buffer;
- }
-
- private static string FixItalics(string text)
- {
- var italicOn = false;
- var sb = new StringBuilder();
- foreach (var line in HtmlUtil.FixInvalidItalicTags(text).SplitToLines())
- {
- var s = line;
- if (italicOn && !s.TrimStart().StartsWith("", StringComparison.Ordinal))
- {
- s = "" + s;
- }
-
- var endTagIndex = s.LastIndexOf("", StringComparison.Ordinal);
- if (s.LastIndexOf("", StringComparison.Ordinal) > endTagIndex)
- {
- italicOn = true;
- }
- else if (endTagIndex >= 0)
- {
- italicOn = false;
- }
-
- if (italicOn)
- {
- sb.AppendLine(s + "");
- }
- else
- {
- sb.AppendLine(s);
- }
- }
-
- return sb.ToString().TrimEnd();
- }
-
- private static string EncodeText(string text, Encoding encoding, string displayStandardCode)
- {
- // newline
- var newline = encoding.GetString(new byte[] { 0x8a, 0x8a });
- if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
- {
- newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a, 0x0d, 0x0b, 0x0b }); // 0a==end box, 0d==double height, 0b==start box
- }
- else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox)
- {
- newline = "\u000a\u000a" +
- string.Empty.PadLeft(Configuration.Settings.SubtitleSettings.EbuStlNewLineRows, '\u008a') +
- encoding.GetString(new byte[] { 0x0b, 0x0b }); // 0a==end box, 0b==start box
- }
- else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
- {
- newline = encoding.GetString(new byte[] { 0x8a, 0x8a, 0x0d, 0x0d }); // 0d==double height
- }
-
- if (displayStandardCode == "0") // 0=Open subtitling
- {
- newline = encoding.GetString(new byte[] { 0x8A }); //8Ah=CR/LF
- }
-
- var lastColor = string.Empty;
- var sb = new StringBuilder();
- var list = text.SplitToLines();
- for (var index = 0; index < list.Count; index++)
- {
- if (index > 0)
- {
- sb.Append(newline);
- if (displayStandardCode != "0" && !string.IsNullOrEmpty(lastColor))
- {
- sb.Append(lastColor);
- }
- }
-
- var line = list[index];
- var i = 0;
- while (i < line.Length)
- {
- var newStart = line.Substring(i);
- if (newStart.StartsWith("', i);
- if (end > 0)
- {
- if (displayStandardCode != "0")
- {
- lastColor = GetColor(encoding, line, i);
- sb.Append(lastColor);
- }
-
- i = end + 1;
- }
- }
- else if (newStart == "")
- {
- i += "".Length;
- lastColor = string.Empty;
- }
- else if (newStart.StartsWith("", StringComparison.OrdinalIgnoreCase))
- {
- if (displayStandardCode != "0")
- {
- sb.Append(encoding.GetString(new byte[] { 0x07 })); // white
- }
-
- i += "".Length;
- }
- else
- {
- sb.Append(line.Substring(i, 1));
- i++;
- }
- }
- }
-
- if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && displayStandardCode != "0")
- {
- sb.Append(encoding.GetString(new byte[] { 0x0a, 0x0a })); //a=end box
- }
-
- return sb.ToString();
- }
-
- private static string GetColor(Encoding encoding, string line, int i)
- {
- var end = line.IndexOf('>', i);
- if (end > 0)
- {
- string f = line.Substring(i, end - i);
- if (f.Contains(" color=", StringComparison.OrdinalIgnoreCase))
- {
- var colorStart = f.IndexOf(" color=", StringComparison.OrdinalIgnoreCase);
- if (line.IndexOf('"', colorStart + " color=".Length + 1) > 0)
- {
- var colorEnd = f.IndexOf('"', colorStart + " color=".Length + 1);
- if (colorStart > 1)
- {
- string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7));
- color = color.Trim('\'');
- color = color.Trim('\"');
- color = color.Trim('#');
- return GetNearestEbuColorCode(color, encoding);
- }
- }
- }
- }
- return string.Empty;
- }
-
- private static string GetNearestEbuColorCode(string color, Encoding encoding)
- {
- color = color.ToLowerInvariant();
- if (color == "black" || color == "000000")
- {
- return encoding.GetString(new byte[] { 0x00 }); // black
- }
-
- if (color == "red" || color == "ff0000")
- {
- return encoding.GetString(new byte[] { 0x01 }); // red
- }
-
- if (color == "green" || color == "00ff00")
- {
- return encoding.GetString(new byte[] { 0x02 }); // green
- }
-
- if (color == "yellow" || color == "ffff00")
- {
- return encoding.GetString(new byte[] { 0x03 }); // yellow
- }
-
- if (color == "blue" || color == "0000ff")
- {
- return encoding.GetString(new byte[] { 0x04 }); // blue
- }
-
- if (color == "magenta" || color == "ff00ff")
- {
- return encoding.GetString(new byte[] { 0x05 }); // magenta
- }
-
- if (color == "cyan" || color == "00ffff")
- {
- return encoding.GetString(new byte[] { 0x06 }); // cyan
- }
-
- if (color == "white" || color == "ffffff")
- {
- return encoding.GetString(new byte[] { 0x07 }); // white
- }
-
- if (color.Length == 6)
- {
- if (RegExprColor.IsMatch(color))
- {
- const int maxDiff = 130;
- int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
- int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
- int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
- if (r < maxDiff && g < maxDiff && b < maxDiff)
- {
- return encoding.GetString(new byte[] { 0x00 }); // black
- }
-
- if (r > 255 - maxDiff && g < maxDiff && b < maxDiff)
- {
- return encoding.GetString(new byte[] { 0x01 }); // red
- }
-
- if (r < maxDiff && g > 255 - maxDiff && b < maxDiff)
- {
- return encoding.GetString(new byte[] { 0x02 }); // green
- }
-
- if (r > 255 - maxDiff && g > 255 - maxDiff && b < maxDiff)
- {
- return encoding.GetString(new byte[] { 0x03 }); // yellow
- }
-
- if (r < maxDiff && g < maxDiff && b > 255 - maxDiff)
- {
- return encoding.GetString(new byte[] { 0x04 }); // blue
- }
-
- if (r > 255 - maxDiff && g < maxDiff && b > 255 - maxDiff)
- {
- return encoding.GetString(new byte[] { 0x05 }); // magenta
- }
-
- if (r < maxDiff && g > 255 - maxDiff && b > 255 - maxDiff)
- {
- return encoding.GetString(new byte[] { 0x06 }); // cyan
- }
-
- if (r > 255 - maxDiff && g > 255 - maxDiff && b > 255 - maxDiff)
- {
- return encoding.GetString(new byte[] { 0x07 }); // white
- }
- }
- }
- return string.Empty;
- }
-
- private static string ReplaceSpecialCharactersWithTwoByteEncoding(char ch, string specialCharacter, string originalCharacters, string newCharacters)
- {
- if (originalCharacters.Length != newCharacters.Length)
- {
- throw new ArgumentException("originalCharacters and newCharacters must have equal length");
- }
-
- for (var i = 0; i < newCharacters.Length; i++)
- {
- if (originalCharacters[i] == ch)
- {
- return specialCharacter + newCharacters[i];
- }
- }
- return ch.ToString();
- }
-
- public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate, out byte extraSeconds)
- {
- extraSeconds = 0;
- var fr = Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
- if (fr >= frameRate)
- {
- fr = 0;
- extraSeconds = 1;
- }
-
- return (byte)fr;
- }
- }
-
- public override string Extension => ".stl";
-
- public const string NameOfFormat = "EBU STL";
-
- public override string Name => NameOfFormat;
-
- public bool Save(string fileName, Subtitle subtitle)
- {
- return Save(fileName, subtitle, false);
- }
-
- public bool Save(string fileName, Subtitle subtitle, bool batchMode, EbuGeneralSubtitleInformation header = null)
- {
- using (var ms = new MemoryStream())
- {
- var ok = Save(fileName, ms, subtitle, batchMode, header);
- if (ok)
- {
- ms.Position = 0;
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
- {
- ms.CopyTo(fs);
- }
- }
- return ok;
- }
- }
-
- public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode, EbuGeneralSubtitleInformation header)
- {
- if (header == null)
- {
- header = new EbuGeneralSubtitleInformation { LanguageCode = AutoDetectLanguageCode(subtitle) };
- }
-
- if (EbuUiHelper == null)
- {
- return false;
- }
-
- if (subtitle.Header != null && subtitle.Header.Length == 1024 && (subtitle.Header.Contains("STL24") || subtitle.Header.Contains("STL25") || subtitle.Header.Contains("STL29") || subtitle.Header.Contains("STL30")))
- {
- header = ReadHeader(GetEncoding(subtitle.Header.Substring(0, 3)).GetBytes(subtitle.Header));
- EbuUiHelper.Initialize(header, EbuUiHelper.JustificationCode, null, subtitle);
- }
- else
- {
- EbuUiHelper.Initialize(header, EbuUiHelper.JustificationCode, fileName, subtitle);
- }
-
- if (!batchMode && !EbuUiHelper.ShowDialogOk())
- {
- return false;
- }
-
- header.TotalNumberOfSubtitles = subtitle.Paragraphs.Count.ToString("D5"); // seems to be 1 higher than actual number of subtitles
- header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles;
-
- var today = $"{DateTime.Now:yyMMdd}";
- if (today.Length == 6)
- {
- header.CreationDate = today;
- header.RevisionDate = today;
- }
-
- var firstParagraph = subtitle.GetParagraphOrDefault(0);
- if (firstParagraph != null)
- {
- var tc = firstParagraph.StartTime;
- var frames = EbuTextTimingInformation.GetFrameFromMilliseconds(tc.Milliseconds, header.FrameRate, out var extraSeconds);
- var firstTimeCode = $"{tc.Hours:00}{tc.Minutes:00}{tc.Seconds + extraSeconds:00}{frames:00}";
- if (firstTimeCode.Length == 8)
- {
- header.TimeCodeFirstInCue = firstTimeCode;
- }
- }
-
- var buffer = GetEncoding(header.CodePageNumber).GetBytes(header.ToString());
- stream.Write(buffer, 0, buffer.Length);
-
- var subtitleNumber = 0;
- foreach (var p in subtitle.Paragraphs)
- {
- var tti = new EbuTextTimingInformation();
-
- if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out var rows))
- {
- rows = 23;
- }
-
- if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
- {
- rows = 23;
- }
- else if (header.DisplayStandardCode == "0" && header.MaximumNumberOfDisplayableRows == "02") // open subtitling
- {
- rows = 15;
- }
-
- var text = p.Text.Trim(Utilities.NewLineChars);
- if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- tti.VerticalPosition = (byte)Configuration.Settings.SubtitleSettings.EbuStlMarginTop; // top (vertical)
- if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
- {
- tti.VerticalPosition++;
- }
- }
- else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
- {
- tti.VerticalPosition = (byte)(rows / 2); // middle (vertical)
- }
- else
- {
- var numberOfLineBreaks = Math.Max(0, Utilities.GetNumberOfLines(text) - 1);
- var startRow = rows - Configuration.Settings.SubtitleSettings.EbuStlMarginBottom -
- numberOfLineBreaks * Configuration.Settings.SubtitleSettings.EbuStlNewLineRows;
- if (startRow < 0)
- {
- startRow = 0;
- }
-
- tti.VerticalPosition = (byte)startRow; // bottom (vertical)
- }
-
- tti.JustificationCode = EbuUiHelper.JustificationCode; // use default justification
- if (text.StartsWith("{\\an1}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an7}", StringComparison.Ordinal))
- {
- tti.JustificationCode = 1; // 01h=left-justified text
- }
- else if (text.StartsWith("{\\an3}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
- {
- tti.JustificationCode = 3; // 03h=right-justified
- }
- else if (text.StartsWith("{\\an2}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal))
- {
- tti.JustificationCode = 2; // 02h=centred text
- }
-
- // replace some unsupported characters
- text = text.Replace("„", "\""); // lower quote
- text = text.Replace("‚", "’"); // lower apostrophe
- text = text.Replace("♫", "♪"); // only music single note supported
- text = text.Replace("…", "..."); // fix Unicode ellipsis
-
- tti.SubtitleNumber = (ushort)subtitleNumber;
- tti.TextField = text;
- int startTag = tti.TextField.IndexOf('}');
- if (tti.TextField.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
- {
- tti.TextField = tti.TextField.Remove(0, startTag + 1);
- }
-
- if (!p.StartTime.IsMaxTime)
- {
- tti.TimeCodeInHours = p.StartTime.Hours;
- tti.TimeCodeInMinutes = p.StartTime.Minutes;
- tti.TimeCodeInSeconds = p.StartTime.Seconds;
- tti.TimeCodeInMilliseconds = p.StartTime.Milliseconds;
- }
-
- if (!p.EndTime.IsMaxTime)
- {
- tti.TimeCodeOutHours = p.EndTime.Hours;
- tti.TimeCodeOutMinutes = p.EndTime.Minutes;
- tti.TimeCodeOutSeconds = p.EndTime.Seconds;
- tti.TimeCodeOutMilliseconds = p.EndTime.Milliseconds;
- }
-
- buffer = tti.GetBytes(header);
- stream.Write(buffer, 0, buffer.Length);
- subtitleNumber++;
- }
- return true;
- }
-
- private static string AutoDetectLanguageCode(Subtitle subtitle)
- {
- if (subtitle == null || subtitle.Paragraphs.Count == 0)
- {
- return "00"; // Unknown/not applicable
- }
-
- var languageCode = LanguageAutoDetect.AutoDetectGoogleLanguageOrNull(subtitle);
- switch (languageCode)
- {
- case "sq": return "01"; // Albanian
- case "br": return "02"; // Breton
- case "ca": return "03"; // Catalan
- case "hr": return "04"; // Croatian
- case "cy": return "05"; // Welsh
- case "cs": return "06"; // Czech
- case "da": return "07"; // Danish
- case "de": return "08"; // German
- case "en": return "09"; // English
- case "es": return "0A"; // Spanish
- case "eo": return "0B"; // Esperanto
- case "et": return "0C"; // Estonian
- case "eu": return "0D"; // Basque
- case "fo": return "0E"; // Faroese
- case "fr": return "0F"; // French
- case "fy": return "10"; // Frisian
- case "ga": return "11"; // Irish
- case "gd": return "12"; // Gaelic
- case "gl": return "13"; // Galician
- case "is": return "14"; // Icelandic
- case "it": return "15"; // Italian
- case "Lappish": return "16"; // Lappish
- case "la": return "17"; // Latin
- case "lv": return "18"; // Latvian":
- case "lb": return "19"; // Luxembourgi
- case "lt": return "1A"; // Lithuanian
- case "hu": return "1B"; // Hungarian
- case "mt": return "1C"; // Maltese
- case "nl": return "1D"; // Dutch
- case "nb": return "1E"; // Norwegian
- case "oc": return "1F"; // Occitan
- case "pl": return "20"; // Polish
- case "pt": return "21"; // Portuguese
- case "ro": return "22"; // Romanian
- case "rm": return "23"; // Romansh
- case "sr": return "24"; // Serbian
- case "sk": return "25"; // Slovak
- case "sl": return "26"; // Slovenian
- case "fi": return "27"; // Finnish
- case "sv": return "28"; // Swedish
- case "tr": return "29"; // Turkish
- case "Flemish": return "2A"; // Flemish
- case "Wallon": return "2B"; // Wallon
- }
-
- return "09"; // English - default
- }
-
- public override bool IsMine(List lines, string fileName)
- {
- if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
- {
- var fi = new FileInfo(fileName);
- if (fi.Length >= 1024 + 128 && fi.Length < 2048000) // not too small or too big
- {
- try
- {
- byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
- EbuGeneralSubtitleInformation header = ReadHeader(buffer);
- if (header.DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL30", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal) ||
- header.DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal) ||
- "012 ".Contains(header.DisplayStandardCode) && "437|850|860|863|865".Contains(header.CodePageNumber))
- {
- return Utilities.IsInteger(header.CodePageNumber) || fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase);
- }
- }
- catch
- {
- return false;
- }
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return "Not supported!";
- }
-
- public void LoadSubtitle(Subtitle subtitle, byte[] buffer)
- {
- subtitle.Paragraphs.Clear();
- subtitle.Header = null;
- var header = ReadHeader(buffer);
- subtitle.Header = Encoding.UTF8.GetString(buffer);
- Paragraph last = null;
- byte lastExtensionBlockNumber = 0xff;
- JustificationCodes = new List();
- VerticalPositions = new List();
- Configuration.Settings.General.CurrentFrameRate = header.FrameRate;
- foreach (var tti in ReadTextAndTiming(buffer, header))
- {
- if (tti.ExtensionBlockNumber != 0xfe) // FEh : Reserved for User Data
- {
- var p = new Paragraph
- {
- Text = tti.TextField,
- StartTime = new TimeCode(tti.TimeCodeInHours, tti.TimeCodeInMinutes, tti.TimeCodeInSeconds, tti.TimeCodeInMilliseconds),
- EndTime = new TimeCode(tti.TimeCodeOutHours, tti.TimeCodeOutMinutes, tti.TimeCodeOutSeconds, tti.TimeCodeOutMilliseconds),
- MarginV = tti.VerticalPosition.ToString(CultureInfo.InvariantCulture)
- };
-
- if (Math.Abs(p.StartTime.TotalMilliseconds) < 0.01 && Math.Abs(p.EndTime.TotalMilliseconds) < 0.01)
- {
- p.StartTime.TotalMilliseconds = TimeCode.MaxTimeTotalMilliseconds;
- p.EndTime.TotalMilliseconds = TimeCode.MaxTimeTotalMilliseconds;
- }
-
- if (lastExtensionBlockNumber != 0xff && last != null)
- {
- last.Text += p.Text; // merge text
- }
- else
- {
- subtitle.Paragraphs.Add(p);
- last = p;
- }
-
- p.Text = HtmlUtil.FixInvalidItalicTags(p.Text);
- lastExtensionBlockNumber = tti.ExtensionBlockNumber;
- }
- }
- subtitle.Renumber();
- Header = header;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- LoadSubtitle(subtitle, FileUtil.ReadAllBytesShared(fileName));
- }
-
- public static EbuGeneralSubtitleInformation ReadHeader(byte[] buffer)
- {
- var enc = GetEncoding(Encoding.ASCII.GetString(buffer, 0, 3));
- var header = new EbuGeneralSubtitleInformation
- {
- CodePageNumber = enc.GetString(buffer, 0, 3),
- DiskFormatCode = enc.GetString(buffer, 3, 8),
- DisplayStandardCode = enc.GetString(buffer, 11, 1),
- CharacterCodeTableNumber = enc.GetString(buffer, 12, 2),
- LanguageCode = enc.GetString(buffer, 14, 2),
- OriginalProgrammeTitle = enc.GetString(buffer, 16, 32),
- OriginalEpisodeTitle = enc.GetString(buffer, 48, 32),
- TranslatedProgrammeTitle = enc.GetString(buffer, 80, 32),
- TranslatedEpisodeTitle = enc.GetString(buffer, 112, 32),
- TranslatorsName = enc.GetString(buffer, 144, 32),
- TranslatorsContactDetails = enc.GetString(buffer, 176, 32),
- SubtitleListReferenceCode = enc.GetString(buffer, 208, 16),
- CreationDate = enc.GetString(buffer, 224, 6),
- RevisionDate = enc.GetString(buffer, 230, 6),
- RevisionNumber = enc.GetString(buffer, 236, 2),
- TotalNumberOfTextAndTimingInformationBlocks = enc.GetString(buffer, 238, 5),
- TotalNumberOfSubtitles = enc.GetString(buffer, 243, 5),
- TotalNumberOfSubtitleGroups = enc.GetString(buffer, 248, 3),
- MaximumNumberOfDisplayableCharactersInAnyTextRow = enc.GetString(buffer, 251, 2),
- MaximumNumberOfDisplayableRows = enc.GetString(buffer, 253, 2),
- TimeCodeStatus = enc.GetString(buffer, 255, 1),
- TimeCodeStartOfProgramme = enc.GetString(buffer, 256, 8),
- CountryOfOrigin = enc.GetString(buffer, 274, 3),
- SpareBytes = enc.GetString(buffer, 373, 75),
- UserDefinedArea = enc.GetString(buffer, 448, 576)
- };
- return header;
- }
-
- private static Encoding GetEncoding(string codePageNumber)
- {
- try
- {
- return Encoding.GetEncoding(int.TryParse(codePageNumber, out int cp) ? cp : 437);
- }
- catch (NotSupportedException)
- {
- return Encoding.GetEncoding(437);
- }
- }
-
- ///
- /// Get text with regard code page from header
- ///
- /// Skip next character
- /// EBU header
- /// data buffer
- /// index to current byte in buffer
- /// Character at index
- private static string GetCharacter(out bool skipNext, EbuGeneralSubtitleInformation header, byte[] buffer, int index)
- {
- skipNext = false;
-
- if (header.LanguageCode == LanguageCodeChinese)
- {
- skipNext = true;
- return Encoding.GetEncoding(1200).GetString(buffer, index, 2); // 16-bit Unicode
- }
-
- if (header.CharacterCodeTableNumber == "00")
- {
- var b = buffer[index];
- if (b == 0xd3)
- {
- return "©";
- }
-
- if (b == 0xd4)
- {
- return "™";
- }
-
- if (b == 0xd5)
- {
- return "♪";
- }
-
- //note that 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937
- var encoding = Encoding.GetEncoding(20269);
- if (index + 2 > buffer.Length)
- {
- return string.Empty;
- }
-
- var next = encoding.GetString(buffer, index + 1, 1);
- switch (b)
- {
- case 0xc1: // Grave
- skipNext = @"AEIOUaeiou".Contains(next);
- switch (next)
- {
- case "A": return "À";
- case "E": return "È";
- case "I": return "Ì";
- case "O": return "Ò";
- case "U": return "Ù";
- case "a": return "à";
- case "e": return "è";
- case "i": return "ì";
- case "o": return "ò";
- case "u": return "ù";
- }
- return string.Empty;
- case 0xc2: // Acute
- skipNext = @"ACEILNORSUYZacegilnorsuyz".Contains(next);
- switch (next)
- {
- case "A": return "Á";
- case "C": return "Ć";
- case "E": return "É";
- case "I": return "Í";
- case "L": return "Ĺ";
- case "N": return "Ń";
- case "O": return "Ó";
- case "R": return "Ŕ";
- case "S": return "Ś";
- case "U": return "Ú";
- case "Y": return "Ý";
- case "Z": return "Ź";
- case "a": return "á";
- case "c": return "ć";
- case "e": return "é";
- case "g": return "ģ";
- case "i": return "í";
- case "l": return "ĺ";
- case "n": return "ń";
- case "o": return "ó";
- case "r": return "ŕ";
- case "s": return "ś";
- case "u": return "ú";
- case "y": return "ý";
- case "z": return "ź";
- }
- return string.Empty;
- case 0xc3: // Circumflex
- skipNext = @"ACEGHIJOSUWYaceghjosuwyıi".Contains(next);
- switch (next)
- {
- case "A": return "Â";
- case "C": return "Ĉ";
- case "E": return "Ê";
- case "G": return "Ĝ";
- case "H": return "Ĥ";
- case "I": return "Î";
- case "J": return "Ĵ";
- case "O": return "Ô";
- case "S": return "Ŝ";
- case "U": return "Û";
- case "W": return "Ŵ";
- case "Y": return "Ŷ";
- case "a": return "â";
- case "c": return "ĉ";
- case "e": return "ê";
- case "g": return "ĝ";
- case "h": return "ĥ";
- case "j": return "ĵ";
- case "o": return "ô";
- case "s": return "ŝ";
- case "u": return "û";
- case "w": return "ŵ";
- case "y": return "ŷ";
- case "ı": return "ı̂";
- case "i": return "î";
- }
- return string.Empty;
- case 0xc4: // Tilde
- skipNext = @"AINOUainou".Contains(next);
- switch (next)
- {
- case "A": return "Ã";
- case "I": return "Ĩ";
- case "N": return "Ñ";
- case "O": return "Õ";
- case "U": return "Ũ";
- case "a": return "ã";
- case "i": return "ĩ";
- case "n": return "ñ";
- case "o": return "õ";
- case "u": return "ũ";
- }
- return string.Empty;
- case 0xc5: // Macron
- skipNext = @"AEIOUaeiou".Contains(next);
- switch (next)
- {
- case "A": return "Ā";
- case "E": return "Ē";
- case "I": return "Ī";
- case "O": return "Ō";
- case "U": return "Ū";
- case "a": return "ā";
- case "e": return "ē";
- case "i": return "ī";
- case "o": return "ō";
- case "u": return "ū";
- }
- return string.Empty;
- case 0xc6: // Breve
- skipNext = @"AGUagu".Contains(next);
- switch (next)
- {
- case "A": return "Ă";
- case "G": return "Ğ";
- case "U": return "Ŭ";
- case "a": return "ă";
- case "g": return "ğ";
- case "u": return "ŭ";
- }
- return string.Empty;
- case 0xc7: // Dot
- skipNext = @"CEGIZcegiz".Contains(next);
- switch (next)
- {
- case "C": return "Ċ";
- case "E": return "Ė";
- case "G": return "Ġ";
- case "I": return "İ";
- case "Z": return "Ż";
- case "c": return "ċ";
- case "e": return "ė";
- case "g": return "ġ";
- case "i": return "ı";
- case "z": return "ż";
- }
- return string.Empty;
- case 0xc8: // Umlaut or diæresis
- skipNext = @"AEIOUYaeiouy".Contains(next);
- switch (next)
- {
- case "A": return "Ä";
- case "E": return "Ë";
- case "I": return "Ï";
- case "O": return "Ö";
- case "U": return "Ü";
- case "Y": return "Ÿ";
- case "a": return "ä";
- case "e": return "ë";
- case "i": return "ï";
- case "o": return "ö";
- case "u": return "ü";
- case "y": return "ÿ";
- }
- return string.Empty;
- case 0xca: // Ring
- skipNext = @"AUau".Contains(next);
- switch (next)
- {
- case "A": return "Å";
- case "U": return "Ů";
- case "a": return "å";
- case "u": return "ů";
- }
- return string.Empty;
- case 0xcb: // Cedilla
- skipNext = @"CGKLNRSTcklnrst".Contains(next);
- switch (next)
- {
- case "C": return "Ç";
- case "G": return "Ģ";
- case "K": return "Ķ";
- case "L": return "Ļ";
- case "N": return "Ņ";
- case "R": return "Ŗ";
- case "S": return "Ş";
- case "T": return "Ţ";
- case "c": return "ç";
- case "k": return "ķ";
- case "l": return "ļ";
- case "n": return "ņ";
- case "r": return "ŗ";
- case "s": return "ş";
- case "t": return "ţ";
- }
- return string.Empty;
- case 0xcd: // DoubleAcute
- skipNext = @"OUou".Contains(next);
- switch (next)
- {
- case "O": return "Ő";
- case "U": return "Ű";
- case "o": return "ő";
- case "u": return "ű";
- }
- return string.Empty;
- case 0xce: // Ogonek
- skipNext = @"AEIUaeiu".Contains(next);
- switch (next)
- {
- case "A": return "Ą";
- case "E": return "Ę";
- case "I": return "Į";
- case "U": return "Ų";
- case "a": return "ą";
- case "e": return "ę";
- case "i": return "į";
- case "u": return "ų";
- }
- return string.Empty;
- case 0xcf: // Caron
- skipNext = @"CDELNRSTZcdelnrstz".Contains(next);
- switch (next)
- {
- case "C": return "Č";
- case "D": return "Ď";
- case "E": return "Ě";
- case "L": return "Ľ";
- case "N": return "Ň";
- case "R": return "Ř";
- case "S": return "Š";
- case "T": return "Ť";
- case "Z": return "Ž";
- case "c": return "č";
- case "d": return "ď";
- case "e": return "ě";
- case "l": return "ľ";
- case "n": return "ň";
- case "r": return "ř";
- case "s": return "š";
- case "t": return "ť";
- case "z": return "ž";
- }
- return string.Empty;
- default:
- return encoding.GetString(buffer, index, 1);
- }
- }
-
- if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988
- {
- return Encoding.GetEncoding("ISO-8859-5").GetString(buffer, index, 1);
- }
-
- if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987
- {
- return Encoding.GetEncoding("ISO-8859-6").GetString(buffer, index, 1);
- }
-
- if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987
- {
- return Encoding.GetEncoding("ISO-8859-7").GetString(buffer, index, 1); // or ISO-8859-1 ?
- }
-
- if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988
- {
- return Encoding.GetEncoding("ISO-8859-8").GetString(buffer, index, 1);
- }
-
- return string.Empty;
- }
-
- ///
- /// Read Text and Timing Information (TTI) block.
- /// Each Text and Timing Information (TTI) block consists of 128 bytes.
- ///
- private IEnumerable ReadTextAndTiming(byte[] buffer, EbuGeneralSubtitleInformation header)
- {
- const int startOfTextAndTimingBlock = 1024;
- const int ttiSize = 128;
- const byte italicsOn = 0x80;
- const byte italicsOff = 0x81;
- const byte underlineOn = 0x82;
- const byte underlineOff = 0x83;
- const byte boxingOn = 0x84;
- const byte boxingOff = 0x85;
-
- var list = new List();
- int index = startOfTextAndTimingBlock;
- while (index + ttiSize <= buffer.Length)
- {
- var tti = new EbuTextTimingInformation
- {
- SubtitleGroupNumber = buffer[index],
- SubtitleNumber = (ushort)(buffer[index + 2] * 256 + buffer[index + 1]),
- ExtensionBlockNumber = buffer[index + 3],
- CumulativeStatus = buffer[index + 4],
- TimeCodeInHours = buffer[index + 5 + 0],
- TimeCodeInMinutes = buffer[index + 5 + 1],
- TimeCodeInSeconds = buffer[index + 5 + 2],
- TimeCodeInMilliseconds = FramesToMillisecondsMax999(buffer[index + 5 + 3]),
- TimeCodeOutHours = buffer[index + 9 + 0],
- TimeCodeOutMinutes = buffer[index + 9 + 1],
- TimeCodeOutSeconds = buffer[index + 9 + 2],
- TimeCodeOutMilliseconds = FramesToMillisecondsMax999(buffer[index + 9 + 3]),
- VerticalPosition = buffer[index + 13],
- JustificationCode = buffer[index + 14],
- CommentFlag = buffer[index + 15]
- };
- VerticalPositions.Add(tti.VerticalPosition);
- JustificationCodes.Add(tti.JustificationCode);
-
- // Text block
- // - has a fixed length of 112 byte
- // - 8Ah = new line
- // - unused space = 8Fh
- int i = index + 16; // text block start at 17th byte (index 16)
- var open = header.DisplayStandardCode != "1" && header.DisplayStandardCode != "2";
- var closed = header.DisplayStandardCode != "0";
- int max = i + 112;
- var sb = new StringBuilder();
- while (i < max)
- {
- var b = buffer[i];
- if (b <= 0x1f) // Closed - Teletext control codes
- {
- if (closed)
- {
- var tag = GetColorOrTag(b);
- if (!string.IsNullOrEmpty(tag))
- {
- sb.Append(tag);
- }
- }
- }
- else if (b >= 0x20 && b <= 0x7f) // Both - Character codes
- {
- var ch = GetCharacter(out var skipNext, header, buffer, i);
- sb.Append(ch);
- if (skipNext)
- {
- i++;
- }
- }
- else if (b >= 0x80 && b <= 0x85) // Open - italic/underline/boxing
- {
- if (open)
- {
- if (b == italicsOn && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- else if (b == italicsOff && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- else if (b == underlineOn && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- else if (b == underlineOff && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- else if (b == boxingOn && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- else if (b == boxingOff && header.LanguageCode != LanguageCodeChinese)
- {
- sb.Append("");
- }
- }
- }
- else if (b >= 0x86 && b <= 0x89) // Both - Reserved for future use
- {
- }
- else if (b == 0x8a) // Both - CR/LF
- {
- sb.AppendLine();
- }
- else if (b >= 0x8b && b <= 0x8e) // Both - Reserved for future use
- {
- }
- else if (b == 0x8f) // Both - unused space
- {
- }
- else if (b >= 0x90 && b <= 0x9f) // Both - Reserved for future use
- {
- }
- else if (b >= 0xa1 && b <= 0xff) // Both - Character codes
- {
- var ch = GetCharacter(out var skipNext, header, buffer, i);
- sb.Append(ch);
- if (skipNext)
- {
- i++;
- }
- }
- i++;
- }
- tti.TextField = FixSpacesAndTags(sb.ToString());
-
- if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out var rows))
- {
- rows = 23;
- }
-
- if (tti.VerticalPosition < 3)
- {
- if (tti.JustificationCode == 1) // left
- {
- tti.TextField = "{\\an7}" + tti.TextField;
- }
- else if (tti.JustificationCode == 3) // right
- {
- tti.TextField = "{\\an9}" + tti.TextField;
- }
- else
- {
- tti.TextField = "{\\an8}" + tti.TextField;
- }
- }
- else if (tti.VerticalPosition <= rows / 2 + 1)
- {
- if (tti.JustificationCode == 1) // left
- {
- tti.TextField = "{\\an4}" + tti.TextField;
- }
- else if (tti.JustificationCode == 3) // right
- {
- tti.TextField = "{\\an6}" + tti.TextField;
- }
- else
- {
- tti.TextField = "{\\an5}" + tti.TextField;
- }
- }
- else
- {
- if (tti.JustificationCode == 1) // left
- {
- tti.TextField = "{\\an1}" + tti.TextField;
- }
- else if (tti.JustificationCode == 3) // right
- {
- tti.TextField = "{\\an3}" + tti.TextField;
- }
- }
- index += ttiSize;
- list.Add(tti);
- }
- return list;
- }
-
- private static string GetColorOrTag(byte b)
- {
- switch (b)
- {
- case 0x00:
- return "";
- case 0x01:
- return "";
- case 0x02:
- return "";
- case 0x03:
- return "";
- case 0x04:
- return "";
- case 0x05:
- return "";
- case 0x06:
- return "";
- case 0x07:
- return "";
- //case 0x0a:
- // return "
";
- //case 0x0b:
- // return "";
- }
- return null;
- }
-
- private static string FixSpacesAndTags(string text)
- {
- text = text.Trim();
- while (text.Contains(" "))
- {
- text = text.Replace(" ", " ");
- }
-
- var match = FontTagsNoSpace1.Match(text);
- while (match.Success)
- {
- text = text.Remove(match.Index, match.Length).Insert(match.Index, match.Value.Replace(" ", string.Empty).Contains("", string.Empty);
- }
-
- while (text.Contains(Environment.NewLine + Environment.NewLine))
- {
- text = text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
- }
-
- var lines = text.SplitToLines();
-
- // fix multi font tags, e.g. a color in the middle of a line
- for (var index = 0; index < lines.Count; index++)
- {
- var whiteTag = "";
- var line = lines[index];
- var changed = false;
- var count = Utilities.CountTagInText(line, " 1)
- {
- count = 0;
- var endTags = 0;
- var idx = line.IndexOf(" 0)
- {
- count++;
- var start = line.Substring(idx);
- if (count == 1 && start.StartsWith(whiteTag))
- {
- line = line.Remove(idx, whiteTag.Length);
- idx--;
- changed = true;
- lines[index] = line;
- }
- else if (count > 1 && start.StartsWith(whiteTag))
- {
- line = line.Remove(idx, whiteTag.Length).Insert(idx, "");
- changed = true;
- lines[index] = line;
- endTags++;
- count--;
- }
- else if (count > 1 && count > endTags + 1 && !start.StartsWith(whiteTag))
- {
- line = line.Insert(idx, "");
- changed = true;
- lines[index] = line;
- idx += "".Length;
- endTags++;
- }
- idx = line.IndexOf(" 0)
- {
- sb.Append(s);
- if (count == 1 && !s.Contains(""))
- {
- sb.Append("");
- }
-
- sb.AppendLine();
- }
- }
-
- text = sb.ToString().TrimEnd();
-
- while (text.Contains(Environment.NewLine + " "))
- {
- text = text.Replace(Environment.NewLine + " ", Environment.NewLine);
- }
-
- // remove starting white spaces
- match = FontTagsStartSpace.Match(text);
- while (match.Success)
- {
- text = text.Remove(match.Index + match.Length - 1, 1);
- match = FontTagsStartSpace.Match(text);
- }
-
- // remove starting white spaces on 2+ line
- match = FontTagsNewLineSpace.Match(text);
- while (match.Success)
- {
- text = text.Remove(match.Index + match.Length - 1, 1);
- match = FontTagsNewLineSpace.Match(text);
- }
-
- text = text.Replace(" ", " ");
-
- text = HtmlUtil.FixInvalidItalicTags(text);
-
- return text;
- }
-
- public override bool IsTextBased => false;
-
- public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
- {
- return Save(fileName, stream, subtitle, batchMode, null);
- }
- }
-}
+using Nikse.SubtitleEdit.Core.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ ///
+ /// EBU Subtitling data exchange format
+ ///
+ public class Ebu : SubtitleFormat, IBinaryPersistableSubtitle
+ {
+ private static readonly Regex FontTagsNoSpace1 = new Regex("[a-zA-z.!?][a-zA-Z-]", RegexOptions.Compiled);
+ private static readonly Regex FontTagsNoSpace2 = new Regex("[a-zA-z.!?][a-zA-Z-]", RegexOptions.Compiled);
+
+ private static readonly Regex FontTagsStartSpace = new Regex("^ ", RegexOptions.Compiled); // " "
+ private static readonly Regex FontTagsNewLineSpace = new Regex("[\r\n]+ ", RegexOptions.Compiled); // "\r\n "
+
+ private const string LanguageCodeChinese = "75";
+
+ public interface IEbuUiHelper
+ {
+ void Initialize(EbuGeneralSubtitleInformation header, byte justificationCode, string fileName, Subtitle subtitle);
+ bool ShowDialogOk();
+ byte JustificationCode { get; set; }
+ }
+
+ public static IEbuUiHelper EbuUiHelper { get; set; }
+
+ private static readonly Regex RegExprColor = new Regex(@"^[a-f0-9]{6}$", RegexOptions.Compiled);
+
+ public List VerticalPositions = new List();
+ public List JustificationCodes = new List();
+
+ public EbuGeneralSubtitleInformation Header;
+
+ ///
+ /// GSI block (1024 bytes)
+ ///
+ public class EbuGeneralSubtitleInformation
+ {
+ public string CodePageNumber { get; set; } // 0..2
+ public string DiskFormatCode { get; set; } // 3..10
+ public double FrameRateFromSaveDialog { get; set; }
+ public string DisplayStandardCode { get; set; } // 11
+ public string CharacterCodeTableNumber { get; set; } // 12..13
+ public string LanguageCode { get; set; } // 14..15
+ public string OriginalProgrammeTitle { get; set; } // 16..47
+ public string OriginalEpisodeTitle { get; set; }
+ public string TranslatedProgrammeTitle { get; set; }
+ public string TranslatedEpisodeTitle { get; set; }
+ public string TranslatorsName { get; set; }
+ public string TranslatorsContactDetails { get; set; }
+ public string SubtitleListReferenceCode { get; set; }
+ public string CreationDate { get; set; }
+ public string RevisionDate { get; set; }
+ public string RevisionNumber { get; set; }
+ public string TotalNumberOfTextAndTimingInformationBlocks { get; set; }
+ public string TotalNumberOfSubtitles { get; set; }
+ public string TotalNumberOfSubtitleGroups { get; set; }
+ public string MaximumNumberOfDisplayableCharactersInAnyTextRow { get; set; }
+ public string MaximumNumberOfDisplayableRows { get; set; }
+ public string TimeCodeStatus { get; set; }
+ public string TimeCodeStartOfProgramme { get; set; }
+ public string TimeCodeFirstInCue { get; set; }
+ public string TotalNumberOfDisks { get; set; }
+ public string DiskSequenceNumber { get; set; }
+ public string CountryOfOrigin { get; set; }
+ public string Publisher { get; set; }
+ public string EditorsName { get; set; }
+ public string EditorsContactDetails { get; set; }
+ public string SpareBytes { get; set; }
+ public string UserDefinedArea { get; set; }
+
+ public double FrameRate
+ {
+ get
+ {
+ if (FrameRateFromSaveDialog > 20)
+ {
+ return FrameRateFromSaveDialog;
+ }
+
+ if (DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal))
+ {
+ return 23.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal))
+ {
+ return 24.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal))
+ {
+ return 25.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal))
+ {
+ return 29.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal))
+ {
+ return 35.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal))
+ {
+ return 48.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal))
+ {
+ return 50.0;
+ }
+
+ if (DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal))
+ {
+ return 60.0;
+ }
+
+ return 30.0; // should be DiskFormatcode STL30.01
+ }
+ }
+
+ public EbuGeneralSubtitleInformation()
+ {
+ CodePageNumber = "437";
+ DiskFormatCode = "STL25.01";
+ DisplayStandardCode = "0"; // 0=Open subtitling
+ CharacterCodeTableNumber = "00";
+ LanguageCode = "0A";
+ OriginalProgrammeTitle = "No Title ";
+ OriginalEpisodeTitle = " ";
+ TranslatedProgrammeTitle = string.Empty.PadLeft(32, ' ');
+ TranslatedEpisodeTitle = string.Empty.PadLeft(32, ' ');
+ TranslatorsName = string.Empty.PadLeft(32, ' ');
+ TranslatorsContactDetails = string.Empty.PadLeft(32, ' ');
+ SubtitleListReferenceCode = "0 ";
+ CreationDate = "101021";
+ RevisionDate = "101021";
+ RevisionNumber = "01";
+ TotalNumberOfTextAndTimingInformationBlocks = "00725";
+ TotalNumberOfSubtitles = "00725";
+ TotalNumberOfSubtitleGroups = "001";
+ MaximumNumberOfDisplayableCharactersInAnyTextRow = "40";
+ MaximumNumberOfDisplayableRows = "23";
+ TimeCodeStatus = "1";
+ TimeCodeStartOfProgramme = "00000000";
+ TimeCodeFirstInCue = "00000001";
+ TotalNumberOfDisks = "1";
+ DiskSequenceNumber = "1";
+ CountryOfOrigin = "USA";
+ Publisher = string.Empty.PadLeft(32, ' ');
+ EditorsName = string.Empty.PadLeft(32, ' ');
+ EditorsContactDetails = string.Empty.PadLeft(32, ' ');
+ SpareBytes = string.Empty.PadLeft(75, ' ');
+ UserDefinedArea = string.Empty.PadLeft(576, ' ');
+ }
+
+ public override string ToString()
+ {
+ var result = CodePageNumber +
+ DiskFormatCode +
+ DisplayStandardCode +
+ CharacterCodeTableNumber +
+ LanguageCode +
+ OriginalProgrammeTitle +
+ OriginalEpisodeTitle +
+ TranslatedProgrammeTitle +
+ TranslatedEpisodeTitle +
+ TranslatorsName +
+ TranslatorsContactDetails +
+ SubtitleListReferenceCode +
+ CreationDate +
+ RevisionDate +
+ RevisionNumber +
+ TotalNumberOfTextAndTimingInformationBlocks +
+ TotalNumberOfSubtitles +
+ TotalNumberOfSubtitleGroups +
+ MaximumNumberOfDisplayableCharactersInAnyTextRow +
+ MaximumNumberOfDisplayableRows +
+ TimeCodeStatus +
+ TimeCodeStartOfProgramme +
+ TimeCodeFirstInCue +
+ TotalNumberOfDisks +
+ DiskSequenceNumber +
+ CountryOfOrigin +
+ Publisher +
+ EditorsName +
+ EditorsContactDetails +
+ SpareBytes +
+ UserDefinedArea;
+
+ if (result.Length == 1024)
+ {
+ return result;
+ }
+
+ return "Length must be 1024 but is " + result.Length;
+ }
+ }
+
+ ///
+ /// TTI block 128 bytes
+ ///
+ private class EbuTextTimingInformation
+ {
+ public byte SubtitleGroupNumber { get; set; }
+ public ushort SubtitleNumber { get; set; }
+ public byte ExtensionBlockNumber { get; set; }
+ public byte CumulativeStatus { get; set; }
+ public int TimeCodeInHours { get; set; }
+ public int TimeCodeInMinutes { get; set; }
+ public int TimeCodeInSeconds { get; set; }
+ public int TimeCodeInMilliseconds { get; set; }
+ public int TimeCodeOutHours { get; set; }
+ public int TimeCodeOutMinutes { get; set; }
+ public int TimeCodeOutSeconds { get; set; }
+ public int TimeCodeOutMilliseconds { get; set; }
+ public byte VerticalPosition { get; set; }
+ public byte JustificationCode { get; set; }
+ public byte CommentFlag { get; set; }
+ public string TextField { get; set; }
+
+ public EbuTextTimingInformation()
+ {
+ SubtitleGroupNumber = 0;
+ ExtensionBlockNumber = 255;
+ CumulativeStatus = 0;
+ VerticalPosition = 0x16;
+ JustificationCode = 2;
+ CommentFlag = 0;
+ }
+
+ public byte[] GetBytes(EbuGeneralSubtitleInformation header)
+ {
+ var buffer = new byte[128]; // Text and Timing Information (TTI) block consists of 128 bytes
+
+ buffer[0] = SubtitleGroupNumber;
+ var temp = BitConverter.GetBytes(SubtitleNumber);
+ buffer[1] = temp[0];
+ buffer[2] = temp[1];
+ buffer[3] = ExtensionBlockNumber;
+ buffer[4] = CumulativeStatus;
+
+ buffer[5] = (byte)TimeCodeInHours;
+ buffer[6] = (byte)TimeCodeInMinutes;
+ var frames = GetFrameFromMilliseconds(TimeCodeInMilliseconds, header.FrameRate, out var extraSeconds);
+ buffer[7] = (byte)(TimeCodeInSeconds + extraSeconds);
+ buffer[8] = frames;
+
+ buffer[9] = (byte)TimeCodeOutHours;
+ buffer[10] = (byte)TimeCodeOutMinutes;
+ frames = GetFrameFromMilliseconds(TimeCodeOutMilliseconds, header.FrameRate, out extraSeconds);
+ buffer[11] = (byte)(TimeCodeOutSeconds + extraSeconds);
+ buffer[12] = frames;
+
+ buffer[13] = VerticalPosition;
+ buffer[14] = JustificationCode;
+ buffer[15] = CommentFlag;
+
+ var encoding = GetEncoding(header.CodePageNumber);
+ if (header.LanguageCode == LanguageCodeChinese)
+ {
+ var lines = HtmlUtil.RemoveHtmlTags(TextField, true).SplitToLines();
+ var byteList = new List();
+ encoding = Encoding.GetEncoding(1200); // 16-bit Unicode
+ for (var i = 0; i < lines.Count; i++)
+ {
+ var l = lines[i];
+ if (i > 0)
+ { // new line
+ byteList.Add(0);
+ byteList.Add(138);
+ }
+ byteList.AddRange(encoding.GetBytes(l).ToArray());
+ }
+
+ for (var i = 0; i < 112; i++)
+ {
+ if (i < byteList.Count)
+ {
+ buffer[16 + i] = byteList[i];
+ }
+ else
+ {
+ buffer[16 + i] = 0x8f;
+ }
+ }
+
+ return buffer;
+ }
+
+ if (header.CharacterCodeTableNumber == "00")
+ {
+ encoding = Encoding.GetEncoding(20269);
+ // 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937
+
+ var sbTwoChar = new StringBuilder();
+ bool skipNext = false;
+ for (var index = 0; index < TextField.Length; index++)
+ {
+ var ch = TextField[index];
+ if (skipNext)
+ {
+ skipNext = false;
+ }
+ else if (ch == 'ı' && TextField.Substring(index).StartsWith("ı̂")) // extended unicode char - rewritten as simple 'î' - looks the same as "î" but it's not...)
+ {
+ sbTwoChar.Append(encoding.GetString(new byte[] { 0xc3, 0x69 })); // Ãi - simple î
+ skipNext = true;
+ }
+ else if ("ÀÈÌÒÙàèìòù".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc1 }), "ÀÈÌÒÙàèìòù", "AEIOUaeiou"));
+ }
+ else if ("ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc2 }), "ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", "ACEILNORSUYZacegilnorsuyz"));
+ }
+ else if ("ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥĵôŝûŵŷîı̂".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc3 }), "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", "ACEGHIJOSUWYaceghijosuwy"));
+ }
+ else if ("ÃĨÑÕŨãĩñõũ".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc4 }), "ÃĨÑÕŨãĩñõũ", "AINOUainou"));
+ }
+ else if ("ĀĒĪŌŪāēīōū".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc5 }), "ĀĒĪŌŪāēīōū", "AEIOUaeiou"));
+ }
+ else if ("ĂĞŬăğŭ".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc6 }), "ĂĞŬăğŭ", "AGUagu"));
+ }
+ else if ("ĊĖĠİŻċėġıż".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc7 }), "ĊĖĠİŻċėġıż", "CEGIZcegiz"));
+ }
+ else if ("ÄËÏÖÜŸäëïöüÿ".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xc8 }), "ÄËÏÖÜŸäëïöüÿ", "AEIOUYaeiouy"));
+ }
+ else if ("ÅŮåů".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xca }), "ÅŮåů", "AUau"));
+ }
+ else if ("ÇĢĶĻŅŖŞŢçķļņŗşţ".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcb }), "ÇĢĶĻŅŖŞŢçķļņŗşţ", "CGKLNRSTcklnrst"));
+ }
+ else if ("ŐŰőű".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcd }), "ŐŰőű", "OUou"));
+ }
+ else if ("ĄĘĮŲąęįų".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xce }), "ĄĘĮŲąęįų", "AEIUaeiu"));
+ }
+ else if ("ČĎĚĽŇŘŠŤŽčďěľňřšťž".Contains(ch))
+ {
+ sbTwoChar.Append(ReplaceSpecialCharactersWithTwoByteEncoding(ch, encoding.GetString(new byte[] { 0xcf }), "ČĎĚĽŇŘŠŤŽčďěľňřšťž", "CDELNRSTZcdelnrstz"));
+ }
+ else
+ {
+ sbTwoChar.Append(ch);
+ }
+ }
+
+ TextField = sbTwoChar.ToString();
+ }
+ else if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988
+ {
+ encoding = Encoding.GetEncoding("ISO-8859-5");
+ }
+ else if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987
+ {
+ encoding = Encoding.GetEncoding("ISO-8859-6");
+ }
+ else if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987
+ {
+ encoding = Encoding.GetEncoding("ISO-8859-7"); // or ISO-8859-1 ?
+ }
+ else if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988
+ {
+ encoding = Encoding.GetEncoding("ISO-8859-8");
+ }
+
+ // italic/underline
+ var italicsOn = encoding.GetString(new byte[] { 0x80 });
+ var italicsOff = encoding.GetString(new byte[] { 0x81 });
+ var underlineOn = encoding.GetString(new byte[] { 0x82 });
+ var underlineOff = encoding.GetString(new byte[] { 0x83 });
+ var boxingOn = encoding.GetString(new byte[] { 0x84 });
+ var boxingOff = encoding.GetString(new byte[] { 0x85 });
+
+ TextField = FixItalics(TextField);
+
+ TextField = TextField.Replace("", italicsOn);
+ TextField = TextField.Replace("", italicsOn);
+ TextField = TextField.Replace("", italicsOff);
+ TextField = TextField.Replace("", italicsOff);
+ TextField = TextField.Replace("", underlineOn);
+ TextField = TextField.Replace("", underlineOn);
+ TextField = TextField.Replace("", underlineOff);
+ TextField = TextField.Replace("", underlineOff);
+ TextField = TextField.Replace("", boxingOn);
+ TextField = TextField.Replace("", boxingOn);
+ TextField = TextField.Replace("", boxingOff);
+ TextField = TextField.Replace("", boxingOff);
+ if (header.CharacterCodeTableNumber == "00")
+ {
+ TextField = TextField.Replace("©", encoding.GetString(new byte[] { 0xd3 }));
+ TextField = TextField.Replace("™", encoding.GetString(new byte[] { 0xd4 }));
+ TextField = TextField.Replace("♪", encoding.GetString(new byte[] { 0xd5 }));
+ }
+
+ TextField = EncodeText(TextField, encoding, header.DisplayStandardCode);
+ TextField = HtmlUtil.RemoveHtmlTags(TextField, true);
+
+ if (header.DisplayStandardCode != "0") // 0=Open subtitling
+ {
+ if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
+ {
+ TextField = encoding.GetString(new byte[] { 0x0d, 0x0b, 0x0b }) + TextField; // d=double height, b=start box
+ }
+ else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox)
+ {
+ TextField = encoding.GetString(new byte[] { 0x0b, 0x0b }) + TextField; // b=start box
+ }
+ else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
+ {
+ TextField = encoding.GetString(new byte[] { 0x0d }) + TextField; // d=double height
+ }
+ }
+
+ // convert text to bytes
+ var bytes = encoding.GetBytes(TextField);
+
+ // some fixes for bytes
+ if (bytes.Length == TextField.Length)
+ {
+ for (var i = 0; i < bytes.Length; i++)
+ {
+ if (TextField[i] == '#')
+ {
+ bytes[i] = 0x23;
+ }
+ else if (TextField[i] == 'Đ')
+ {
+ bytes[i] = 0xe2;
+ }
+ else if (TextField[i] == '–') // em dash
+ {
+ bytes[i] = 0xd0;
+ }
+ }
+ }
+
+ for (var i = 0; i < 112; i++)
+ {
+ if (i < bytes.Length)
+ {
+ buffer[16 + i] = bytes[i];
+ }
+ else
+ {
+ buffer[16 + i] = 0x8f;
+ }
+ }
+ return buffer;
+ }
+
+ private static string FixItalics(string text)
+ {
+ var italicOn = false;
+ var sb = new StringBuilder();
+ foreach (var line in HtmlUtil.FixInvalidItalicTags(text).SplitToLines())
+ {
+ var s = line;
+ if (italicOn && !s.TrimStart().StartsWith("", StringComparison.Ordinal))
+ {
+ s = "" + s;
+ }
+
+ var endTagIndex = s.LastIndexOf("", StringComparison.Ordinal);
+ if (s.LastIndexOf("", StringComparison.Ordinal) > endTagIndex)
+ {
+ italicOn = true;
+ }
+ else if (endTagIndex >= 0)
+ {
+ italicOn = false;
+ }
+
+ if (italicOn)
+ {
+ sb.AppendLine(s + "");
+ }
+ else
+ {
+ sb.AppendLine(s);
+ }
+ }
+
+ return sb.ToString().TrimEnd();
+ }
+
+ private static string EncodeText(string text, Encoding encoding, string displayStandardCode)
+ {
+ // newline
+ var newline = encoding.GetString(new byte[] { 0x8a, 0x8a });
+ if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
+ {
+ newline = encoding.GetString(new byte[] { 0x0a, 0x0a, 0x8a, 0x8a, 0x0d, 0x0b, 0x0b }); // 0a==end box, 0d==double height, 0b==start box
+ }
+ else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox)
+ {
+ newline = "\u000a\u000a" +
+ string.Empty.PadLeft(Configuration.Settings.SubtitleSettings.EbuStlNewLineRows, '\u008a') +
+ encoding.GetString(new byte[] { 0x0b, 0x0b }); // 0a==end box, 0b==start box
+ }
+ else if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseDoubleHeight)
+ {
+ newline = encoding.GetString(new byte[] { 0x8a, 0x8a, 0x0d, 0x0d }); // 0d==double height
+ }
+
+ if (displayStandardCode == "0") // 0=Open subtitling
+ {
+ newline = encoding.GetString(new byte[] { 0x8A }); //8Ah=CR/LF
+ }
+
+ var lastColor = string.Empty;
+ var sb = new StringBuilder();
+ var list = text.SplitToLines();
+ for (var index = 0; index < list.Count; index++)
+ {
+ if (index > 0)
+ {
+ sb.Append(newline);
+ if (displayStandardCode != "0" && !string.IsNullOrEmpty(lastColor))
+ {
+ sb.Append(lastColor);
+ }
+ }
+
+ var line = list[index];
+ var i = 0;
+ while (i < line.Length)
+ {
+ var newStart = line.Substring(i);
+ if (newStart.StartsWith("', i);
+ if (end > 0)
+ {
+ if (displayStandardCode != "0")
+ {
+ lastColor = GetColor(encoding, line, i);
+ sb.Append(lastColor);
+ }
+
+ i = end + 1;
+ }
+ }
+ else if (newStart == "")
+ {
+ i += "".Length;
+ lastColor = string.Empty;
+ }
+ else if (newStart.StartsWith("", StringComparison.OrdinalIgnoreCase))
+ {
+ if (displayStandardCode != "0")
+ {
+ sb.Append(encoding.GetString(new byte[] { 0x07 })); // white
+ }
+
+ i += "".Length;
+ }
+ else
+ {
+ sb.Append(line.Substring(i, 1));
+ i++;
+ }
+ }
+ }
+
+ if (Configuration.Settings.SubtitleSettings.EbuStlTeletextUseBox && displayStandardCode != "0")
+ {
+ sb.Append(encoding.GetString(new byte[] { 0x0a, 0x0a })); //a=end box
+ }
+
+ return sb.ToString();
+ }
+
+ private static string GetColor(Encoding encoding, string line, int i)
+ {
+ var end = line.IndexOf('>', i);
+ if (end > 0)
+ {
+ string f = line.Substring(i, end - i);
+ if (f.Contains(" color=", StringComparison.OrdinalIgnoreCase))
+ {
+ var colorStart = f.IndexOf(" color=", StringComparison.OrdinalIgnoreCase);
+ if (line.IndexOf('"', colorStart + " color=".Length + 1) > 0)
+ {
+ var colorEnd = f.IndexOf('"', colorStart + " color=".Length + 1);
+ if (colorStart > 1)
+ {
+ string color = f.Substring(colorStart + 7, colorEnd - (colorStart + 7));
+ color = color.Trim('\'');
+ color = color.Trim('\"');
+ color = color.Trim('#');
+ return GetNearestEbuColorCode(color, encoding);
+ }
+ }
+ }
+ }
+ return string.Empty;
+ }
+
+ private static string GetNearestEbuColorCode(string color, Encoding encoding)
+ {
+ color = color.ToLowerInvariant();
+ if (color == "black" || color == "000000")
+ {
+ return encoding.GetString(new byte[] { 0x00 }); // black
+ }
+
+ if (color == "red" || color == "ff0000")
+ {
+ return encoding.GetString(new byte[] { 0x01 }); // red
+ }
+
+ if (color == "green" || color == "00ff00")
+ {
+ return encoding.GetString(new byte[] { 0x02 }); // green
+ }
+
+ if (color == "yellow" || color == "ffff00")
+ {
+ return encoding.GetString(new byte[] { 0x03 }); // yellow
+ }
+
+ if (color == "blue" || color == "0000ff")
+ {
+ return encoding.GetString(new byte[] { 0x04 }); // blue
+ }
+
+ if (color == "magenta" || color == "ff00ff")
+ {
+ return encoding.GetString(new byte[] { 0x05 }); // magenta
+ }
+
+ if (color == "cyan" || color == "00ffff")
+ {
+ return encoding.GetString(new byte[] { 0x06 }); // cyan
+ }
+
+ if (color == "white" || color == "ffffff")
+ {
+ return encoding.GetString(new byte[] { 0x07 }); // white
+ }
+
+ if (color.Length == 6)
+ {
+ if (RegExprColor.IsMatch(color))
+ {
+ const int maxDiff = 130;
+ int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
+ int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
+ int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
+ if (r < maxDiff && g < maxDiff && b < maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x00 }); // black
+ }
+
+ if (r > 255 - maxDiff && g < maxDiff && b < maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x01 }); // red
+ }
+
+ if (r < maxDiff && g > 255 - maxDiff && b < maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x02 }); // green
+ }
+
+ if (r > 255 - maxDiff && g > 255 - maxDiff && b < maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x03 }); // yellow
+ }
+
+ if (r < maxDiff && g < maxDiff && b > 255 - maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x04 }); // blue
+ }
+
+ if (r > 255 - maxDiff && g < maxDiff && b > 255 - maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x05 }); // magenta
+ }
+
+ if (r < maxDiff && g > 255 - maxDiff && b > 255 - maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x06 }); // cyan
+ }
+
+ if (r > 255 - maxDiff && g > 255 - maxDiff && b > 255 - maxDiff)
+ {
+ return encoding.GetString(new byte[] { 0x07 }); // white
+ }
+ }
+ }
+ return string.Empty;
+ }
+
+ private static string ReplaceSpecialCharactersWithTwoByteEncoding(char ch, string specialCharacter, string originalCharacters, string newCharacters)
+ {
+ if (originalCharacters.Length != newCharacters.Length)
+ {
+ throw new ArgumentException("originalCharacters and newCharacters must have equal length");
+ }
+
+ for (var i = 0; i < newCharacters.Length; i++)
+ {
+ if (originalCharacters[i] == ch)
+ {
+ return specialCharacter + newCharacters[i];
+ }
+ }
+ return ch.ToString();
+ }
+
+ public static byte GetFrameFromMilliseconds(int milliseconds, double frameRate, out byte extraSeconds)
+ {
+ extraSeconds = 0;
+ var fr = Math.Round(milliseconds / (TimeCode.BaseUnit / frameRate));
+ if (fr >= frameRate)
+ {
+ fr = 0;
+ extraSeconds = 1;
+ }
+
+ return (byte)fr;
+ }
+ }
+
+ public override string Extension => ".stl";
+
+ public const string NameOfFormat = "EBU STL";
+
+ public override string Name => NameOfFormat;
+
+ public bool Save(string fileName, Subtitle subtitle)
+ {
+ return Save(fileName, subtitle, false);
+ }
+
+ public bool Save(string fileName, Subtitle subtitle, bool batchMode, EbuGeneralSubtitleInformation header = null)
+ {
+ using (var ms = new MemoryStream())
+ {
+ var ok = Save(fileName, ms, subtitle, batchMode, header);
+ if (ok)
+ {
+ ms.Position = 0;
+ using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+ {
+ ms.CopyTo(fs);
+ }
+ }
+ return ok;
+ }
+ }
+
+ public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode, EbuGeneralSubtitleInformation header)
+ {
+ if (header == null)
+ {
+ header = new EbuGeneralSubtitleInformation { LanguageCode = AutoDetectLanguageCode(subtitle) };
+ }
+
+ if (EbuUiHelper == null)
+ {
+ return false;
+ }
+
+ if (subtitle.Header != null && subtitle.Header.Length == 1024 && (subtitle.Header.Contains("STL24") || subtitle.Header.Contains("STL25") || subtitle.Header.Contains("STL29") || subtitle.Header.Contains("STL30")))
+ {
+ header = ReadHeader(GetEncoding(subtitle.Header.Substring(0, 3)).GetBytes(subtitle.Header));
+ EbuUiHelper.Initialize(header, EbuUiHelper.JustificationCode, null, subtitle);
+ }
+ else
+ {
+ EbuUiHelper.Initialize(header, EbuUiHelper.JustificationCode, fileName, subtitle);
+ }
+
+ if (!batchMode && !EbuUiHelper.ShowDialogOk())
+ {
+ return false;
+ }
+
+ header.TotalNumberOfSubtitles = subtitle.Paragraphs.Count.ToString("D5"); // seems to be 1 higher than actual number of subtitles
+ header.TotalNumberOfTextAndTimingInformationBlocks = header.TotalNumberOfSubtitles;
+
+ var today = $"{DateTime.Now:yyMMdd}";
+ if (today.Length == 6)
+ {
+ header.CreationDate = today;
+ header.RevisionDate = today;
+ }
+
+ var firstParagraph = subtitle.GetParagraphOrDefault(0);
+ if (firstParagraph != null)
+ {
+ var tc = firstParagraph.StartTime;
+ var frames = EbuTextTimingInformation.GetFrameFromMilliseconds(tc.Milliseconds, header.FrameRate, out var extraSeconds);
+ var firstTimeCode = $"{tc.Hours:00}{tc.Minutes:00}{tc.Seconds + extraSeconds:00}{frames:00}";
+ if (firstTimeCode.Length == 8)
+ {
+ header.TimeCodeFirstInCue = firstTimeCode;
+ }
+ }
+
+ var buffer = GetEncoding(header.CodePageNumber).GetBytes(header.ToString());
+ stream.Write(buffer, 0, buffer.Length);
+
+ var subtitleNumber = 0;
+ foreach (var p in subtitle.Paragraphs)
+ {
+ var tti = new EbuTextTimingInformation();
+
+ if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out var rows))
+ {
+ rows = 23;
+ }
+
+ if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
+ {
+ rows = 23;
+ }
+ else if (header.DisplayStandardCode == "0" && header.MaximumNumberOfDisplayableRows == "02") // open subtitling
+ {
+ rows = 15;
+ }
+
+ var text = p.Text.Trim(Utilities.NewLineChars);
+ if (text.StartsWith("{\\an7}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ tti.VerticalPosition = (byte)Configuration.Settings.SubtitleSettings.EbuStlMarginTop; // top (vertical)
+ if (header.DisplayStandardCode == "1" || header.DisplayStandardCode == "2") // teletext
+ {
+ tti.VerticalPosition++;
+ }
+ }
+ else if (text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal))
+ {
+ tti.VerticalPosition = (byte)(rows / 2); // middle (vertical)
+ }
+ else
+ {
+ var numberOfLineBreaks = Math.Max(0, Utilities.GetNumberOfLines(text) - 1);
+ var startRow = rows - Configuration.Settings.SubtitleSettings.EbuStlMarginBottom -
+ numberOfLineBreaks * Configuration.Settings.SubtitleSettings.EbuStlNewLineRows;
+ if (startRow < 0)
+ {
+ startRow = 0;
+ }
+
+ tti.VerticalPosition = (byte)startRow; // bottom (vertical)
+ }
+
+ tti.JustificationCode = EbuUiHelper.JustificationCode; // use default justification
+ if (text.StartsWith("{\\an1}", StringComparison.Ordinal) || text.StartsWith("{\\an4}", StringComparison.Ordinal) || text.StartsWith("{\\an7}", StringComparison.Ordinal))
+ {
+ tti.JustificationCode = 1; // 01h=left-justified text
+ }
+ else if (text.StartsWith("{\\an3}", StringComparison.Ordinal) || text.StartsWith("{\\an6}", StringComparison.Ordinal) || text.StartsWith("{\\an9}", StringComparison.Ordinal))
+ {
+ tti.JustificationCode = 3; // 03h=right-justified
+ }
+ else if (text.StartsWith("{\\an2}", StringComparison.Ordinal) || text.StartsWith("{\\an5}", StringComparison.Ordinal) || text.StartsWith("{\\an8}", StringComparison.Ordinal))
+ {
+ tti.JustificationCode = 2; // 02h=centred text
+ }
+
+ // replace some unsupported characters
+ text = text.Replace("„", "\""); // lower quote
+ text = text.Replace("‚", "’"); // lower apostrophe
+ text = text.Replace("♫", "♪"); // only music single note supported
+ text = text.Replace("…", "..."); // fix Unicode ellipsis
+
+ tti.SubtitleNumber = (ushort)subtitleNumber;
+ tti.TextField = text;
+ int startTag = tti.TextField.IndexOf('}');
+ if (tti.TextField.StartsWith("{\\", StringComparison.Ordinal) && startTag > 0 && startTag < 10)
+ {
+ tti.TextField = tti.TextField.Remove(0, startTag + 1);
+ }
+
+ if (!p.StartTime.IsMaxTime)
+ {
+ tti.TimeCodeInHours = p.StartTime.Hours;
+ tti.TimeCodeInMinutes = p.StartTime.Minutes;
+ tti.TimeCodeInSeconds = p.StartTime.Seconds;
+ tti.TimeCodeInMilliseconds = p.StartTime.Milliseconds;
+ }
+
+ if (!p.EndTime.IsMaxTime)
+ {
+ tti.TimeCodeOutHours = p.EndTime.Hours;
+ tti.TimeCodeOutMinutes = p.EndTime.Minutes;
+ tti.TimeCodeOutSeconds = p.EndTime.Seconds;
+ tti.TimeCodeOutMilliseconds = p.EndTime.Milliseconds;
+ }
+
+ buffer = tti.GetBytes(header);
+ stream.Write(buffer, 0, buffer.Length);
+ subtitleNumber++;
+ }
+ return true;
+ }
+
+ private static string AutoDetectLanguageCode(Subtitle subtitle)
+ {
+ if (subtitle == null || subtitle.Paragraphs.Count == 0)
+ {
+ return "00"; // Unknown/not applicable
+ }
+
+ var languageCode = LanguageAutoDetect.AutoDetectGoogleLanguageOrNull(subtitle);
+ switch (languageCode)
+ {
+ case "sq": return "01"; // Albanian
+ case "br": return "02"; // Breton
+ case "ca": return "03"; // Catalan
+ case "hr": return "04"; // Croatian
+ case "cy": return "05"; // Welsh
+ case "cs": return "06"; // Czech
+ case "da": return "07"; // Danish
+ case "de": return "08"; // German
+ case "en": return "09"; // English
+ case "es": return "0A"; // Spanish
+ case "eo": return "0B"; // Esperanto
+ case "et": return "0C"; // Estonian
+ case "eu": return "0D"; // Basque
+ case "fo": return "0E"; // Faroese
+ case "fr": return "0F"; // French
+ case "fy": return "10"; // Frisian
+ case "ga": return "11"; // Irish
+ case "gd": return "12"; // Gaelic
+ case "gl": return "13"; // Galician
+ case "is": return "14"; // Icelandic
+ case "it": return "15"; // Italian
+ case "Lappish": return "16"; // Lappish
+ case "la": return "17"; // Latin
+ case "lv": return "18"; // Latvian":
+ case "lb": return "19"; // Luxembourgi
+ case "lt": return "1A"; // Lithuanian
+ case "hu": return "1B"; // Hungarian
+ case "mt": return "1C"; // Maltese
+ case "nl": return "1D"; // Dutch
+ case "nb": return "1E"; // Norwegian
+ case "oc": return "1F"; // Occitan
+ case "pl": return "20"; // Polish
+ case "pt": return "21"; // Portuguese
+ case "ro": return "22"; // Romanian
+ case "rm": return "23"; // Romansh
+ case "sr": return "24"; // Serbian
+ case "sk": return "25"; // Slovak
+ case "sl": return "26"; // Slovenian
+ case "fi": return "27"; // Finnish
+ case "sv": return "28"; // Swedish
+ case "tr": return "29"; // Turkish
+ case "Flemish": return "2A"; // Flemish
+ case "Wallon": return "2B"; // Wallon
+ }
+
+ return "09"; // English - default
+ }
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
+ {
+ var fi = new FileInfo(fileName);
+ if (fi.Length >= 1024 + 128 && fi.Length < 2048000) // not too small or too big
+ {
+ try
+ {
+ byte[] buffer = FileUtil.ReadAllBytesShared(fileName);
+ EbuGeneralSubtitleInformation header = ReadHeader(buffer);
+ if (header.DiskFormatCode.StartsWith("STL23", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL24", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL25", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL29", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL30", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL35", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL48", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL50", StringComparison.Ordinal) ||
+ header.DiskFormatCode.StartsWith("STL60", StringComparison.Ordinal) ||
+ "012 ".Contains(header.DisplayStandardCode) && "437|850|860|863|865".Contains(header.CodePageNumber))
+ {
+ return Utilities.IsInteger(header.CodePageNumber) || fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return "Not supported!";
+ }
+
+ public void LoadSubtitle(Subtitle subtitle, byte[] buffer)
+ {
+ subtitle.Paragraphs.Clear();
+ subtitle.Header = null;
+ var header = ReadHeader(buffer);
+ subtitle.Header = Encoding.UTF8.GetString(buffer);
+ Paragraph last = null;
+ byte lastExtensionBlockNumber = 0xff;
+ JustificationCodes = new List();
+ VerticalPositions = new List();
+ Configuration.Settings.General.CurrentFrameRate = header.FrameRate;
+ foreach (var tti in ReadTextAndTiming(buffer, header))
+ {
+ if (tti.ExtensionBlockNumber != 0xfe) // FEh : Reserved for User Data
+ {
+ var p = new Paragraph
+ {
+ Text = tti.TextField,
+ StartTime = new TimeCode(tti.TimeCodeInHours, tti.TimeCodeInMinutes, tti.TimeCodeInSeconds, tti.TimeCodeInMilliseconds),
+ EndTime = new TimeCode(tti.TimeCodeOutHours, tti.TimeCodeOutMinutes, tti.TimeCodeOutSeconds, tti.TimeCodeOutMilliseconds),
+ MarginV = tti.VerticalPosition.ToString(CultureInfo.InvariantCulture)
+ };
+
+ if (Math.Abs(p.StartTime.TotalMilliseconds) < 0.01 && Math.Abs(p.EndTime.TotalMilliseconds) < 0.01)
+ {
+ p.StartTime.TotalMilliseconds = TimeCode.MaxTimeTotalMilliseconds;
+ p.EndTime.TotalMilliseconds = TimeCode.MaxTimeTotalMilliseconds;
+ }
+
+ if (lastExtensionBlockNumber != 0xff && last != null)
+ {
+ last.Text += p.Text; // merge text
+ }
+ else
+ {
+ subtitle.Paragraphs.Add(p);
+ last = p;
+ }
+
+ p.Text = HtmlUtil.FixInvalidItalicTags(p.Text);
+ lastExtensionBlockNumber = tti.ExtensionBlockNumber;
+ }
+ }
+ subtitle.Renumber();
+ Header = header;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ LoadSubtitle(subtitle, FileUtil.ReadAllBytesShared(fileName));
+ }
+
+ public static EbuGeneralSubtitleInformation ReadHeader(byte[] buffer)
+ {
+ var enc = GetEncoding(Encoding.ASCII.GetString(buffer, 0, 3));
+ var header = new EbuGeneralSubtitleInformation
+ {
+ CodePageNumber = enc.GetString(buffer, 0, 3),
+ DiskFormatCode = enc.GetString(buffer, 3, 8),
+ DisplayStandardCode = enc.GetString(buffer, 11, 1),
+ CharacterCodeTableNumber = enc.GetString(buffer, 12, 2),
+ LanguageCode = enc.GetString(buffer, 14, 2),
+ OriginalProgrammeTitle = enc.GetString(buffer, 16, 32),
+ OriginalEpisodeTitle = enc.GetString(buffer, 48, 32),
+ TranslatedProgrammeTitle = enc.GetString(buffer, 80, 32),
+ TranslatedEpisodeTitle = enc.GetString(buffer, 112, 32),
+ TranslatorsName = enc.GetString(buffer, 144, 32),
+ TranslatorsContactDetails = enc.GetString(buffer, 176, 32),
+ SubtitleListReferenceCode = enc.GetString(buffer, 208, 16),
+ CreationDate = enc.GetString(buffer, 224, 6),
+ RevisionDate = enc.GetString(buffer, 230, 6),
+ RevisionNumber = enc.GetString(buffer, 236, 2),
+ TotalNumberOfTextAndTimingInformationBlocks = enc.GetString(buffer, 238, 5),
+ TotalNumberOfSubtitles = enc.GetString(buffer, 243, 5),
+ TotalNumberOfSubtitleGroups = enc.GetString(buffer, 248, 3),
+ MaximumNumberOfDisplayableCharactersInAnyTextRow = enc.GetString(buffer, 251, 2),
+ MaximumNumberOfDisplayableRows = enc.GetString(buffer, 253, 2),
+ TimeCodeStatus = enc.GetString(buffer, 255, 1),
+ TimeCodeStartOfProgramme = enc.GetString(buffer, 256, 8),
+ CountryOfOrigin = enc.GetString(buffer, 274, 3),
+ SpareBytes = enc.GetString(buffer, 373, 75),
+ UserDefinedArea = enc.GetString(buffer, 448, 576)
+ };
+ return header;
+ }
+
+ private static Encoding GetEncoding(string codePageNumber)
+ {
+ try
+ {
+ return Encoding.GetEncoding(int.TryParse(codePageNumber, out int cp) ? cp : 437);
+ }
+ catch (NotSupportedException)
+ {
+ return Encoding.GetEncoding(437);
+ }
+ }
+
+ ///
+ /// Get text with regard code page from header
+ ///
+ /// Skip next character
+ /// EBU header
+ /// data buffer
+ /// index to current byte in buffer
+ /// Character at index
+ private static string GetCharacter(out bool skipNext, EbuGeneralSubtitleInformation header, byte[] buffer, int index)
+ {
+ skipNext = false;
+
+ if (header.LanguageCode == LanguageCodeChinese)
+ {
+ skipNext = true;
+ return Encoding.GetEncoding(1200).GetString(buffer, index, 2); // 16-bit Unicode
+ }
+
+ if (header.CharacterCodeTableNumber == "00")
+ {
+ var b = buffer[index];
+ if (b == 0xd3)
+ {
+ return "©";
+ }
+
+ if (b == 0xd4)
+ {
+ return "™";
+ }
+
+ if (b == 0xd5)
+ {
+ return "♪";
+ }
+
+ //note that 0xC1—0xCF combines characters - http://en.wikipedia.org/wiki/ISO/IEC_6937
+ var encoding = Encoding.GetEncoding(20269);
+ if (index + 2 > buffer.Length)
+ {
+ return string.Empty;
+ }
+
+ var next = encoding.GetString(buffer, index + 1, 1);
+ switch (b)
+ {
+ case 0xc1: // Grave
+ skipNext = @"AEIOUaeiou".Contains(next);
+ switch (next)
+ {
+ case "A": return "À";
+ case "E": return "È";
+ case "I": return "Ì";
+ case "O": return "Ò";
+ case "U": return "Ù";
+ case "a": return "à";
+ case "e": return "è";
+ case "i": return "ì";
+ case "o": return "ò";
+ case "u": return "ù";
+ }
+ return string.Empty;
+ case 0xc2: // Acute
+ skipNext = @"ACEILNORSUYZacegilnorsuyz".Contains(next);
+ switch (next)
+ {
+ case "A": return "Á";
+ case "C": return "Ć";
+ case "E": return "É";
+ case "I": return "Í";
+ case "L": return "Ĺ";
+ case "N": return "Ń";
+ case "O": return "Ó";
+ case "R": return "Ŕ";
+ case "S": return "Ś";
+ case "U": return "Ú";
+ case "Y": return "Ý";
+ case "Z": return "Ź";
+ case "a": return "á";
+ case "c": return "ć";
+ case "e": return "é";
+ case "g": return "ģ";
+ case "i": return "í";
+ case "l": return "ĺ";
+ case "n": return "ń";
+ case "o": return "ó";
+ case "r": return "ŕ";
+ case "s": return "ś";
+ case "u": return "ú";
+ case "y": return "ý";
+ case "z": return "ź";
+ }
+ return string.Empty;
+ case 0xc3: // Circumflex
+ skipNext = @"ACEGHIJOSUWYaceghjosuwyıi".Contains(next);
+ switch (next)
+ {
+ case "A": return "Â";
+ case "C": return "Ĉ";
+ case "E": return "Ê";
+ case "G": return "Ĝ";
+ case "H": return "Ĥ";
+ case "I": return "Î";
+ case "J": return "Ĵ";
+ case "O": return "Ô";
+ case "S": return "Ŝ";
+ case "U": return "Û";
+ case "W": return "Ŵ";
+ case "Y": return "Ŷ";
+ case "a": return "â";
+ case "c": return "ĉ";
+ case "e": return "ê";
+ case "g": return "ĝ";
+ case "h": return "ĥ";
+ case "j": return "ĵ";
+ case "o": return "ô";
+ case "s": return "ŝ";
+ case "u": return "û";
+ case "w": return "ŵ";
+ case "y": return "ŷ";
+ case "ı": return "ı̂";
+ case "i": return "î";
+ }
+ return string.Empty;
+ case 0xc4: // Tilde
+ skipNext = @"AINOUainou".Contains(next);
+ switch (next)
+ {
+ case "A": return "Ã";
+ case "I": return "Ĩ";
+ case "N": return "Ñ";
+ case "O": return "Õ";
+ case "U": return "Ũ";
+ case "a": return "ã";
+ case "i": return "ĩ";
+ case "n": return "ñ";
+ case "o": return "õ";
+ case "u": return "ũ";
+ }
+ return string.Empty;
+ case 0xc5: // Macron
+ skipNext = @"AEIOUaeiou".Contains(next);
+ switch (next)
+ {
+ case "A": return "Ā";
+ case "E": return "Ē";
+ case "I": return "Ī";
+ case "O": return "Ō";
+ case "U": return "Ū";
+ case "a": return "ā";
+ case "e": return "ē";
+ case "i": return "ī";
+ case "o": return "ō";
+ case "u": return "ū";
+ }
+ return string.Empty;
+ case 0xc6: // Breve
+ skipNext = @"AGUagu".Contains(next);
+ switch (next)
+ {
+ case "A": return "Ă";
+ case "G": return "Ğ";
+ case "U": return "Ŭ";
+ case "a": return "ă";
+ case "g": return "ğ";
+ case "u": return "ŭ";
+ }
+ return string.Empty;
+ case 0xc7: // Dot
+ skipNext = @"CEGIZcegiz".Contains(next);
+ switch (next)
+ {
+ case "C": return "Ċ";
+ case "E": return "Ė";
+ case "G": return "Ġ";
+ case "I": return "İ";
+ case "Z": return "Ż";
+ case "c": return "ċ";
+ case "e": return "ė";
+ case "g": return "ġ";
+ case "i": return "ı";
+ case "z": return "ż";
+ }
+ return string.Empty;
+ case 0xc8: // Umlaut or diæresis
+ skipNext = @"AEIOUYaeiouy".Contains(next);
+ switch (next)
+ {
+ case "A": return "Ä";
+ case "E": return "Ë";
+ case "I": return "Ï";
+ case "O": return "Ö";
+ case "U": return "Ü";
+ case "Y": return "Ÿ";
+ case "a": return "ä";
+ case "e": return "ë";
+ case "i": return "ï";
+ case "o": return "ö";
+ case "u": return "ü";
+ case "y": return "ÿ";
+ }
+ return string.Empty;
+ case 0xca: // Ring
+ skipNext = @"AUau".Contains(next);
+ switch (next)
+ {
+ case "A": return "Å";
+ case "U": return "Ů";
+ case "a": return "å";
+ case "u": return "ů";
+ }
+ return string.Empty;
+ case 0xcb: // Cedilla
+ skipNext = @"CGKLNRSTcklnrst".Contains(next);
+ switch (next)
+ {
+ case "C": return "Ç";
+ case "G": return "Ģ";
+ case "K": return "Ķ";
+ case "L": return "Ļ";
+ case "N": return "Ņ";
+ case "R": return "Ŗ";
+ case "S": return "Ş";
+ case "T": return "Ţ";
+ case "c": return "ç";
+ case "k": return "ķ";
+ case "l": return "ļ";
+ case "n": return "ņ";
+ case "r": return "ŗ";
+ case "s": return "ş";
+ case "t": return "ţ";
+ }
+ return string.Empty;
+ case 0xcd: // DoubleAcute
+ skipNext = @"OUou".Contains(next);
+ switch (next)
+ {
+ case "O": return "Ő";
+ case "U": return "Ű";
+ case "o": return "ő";
+ case "u": return "ű";
+ }
+ return string.Empty;
+ case 0xce: // Ogonek
+ skipNext = @"AEIUaeiu".Contains(next);
+ switch (next)
+ {
+ case "A": return "Ą";
+ case "E": return "Ę";
+ case "I": return "Į";
+ case "U": return "Ų";
+ case "a": return "ą";
+ case "e": return "ę";
+ case "i": return "į";
+ case "u": return "ų";
+ }
+ return string.Empty;
+ case 0xcf: // Caron
+ skipNext = @"CDELNRSTZcdelnrstz".Contains(next);
+ switch (next)
+ {
+ case "C": return "Č";
+ case "D": return "Ď";
+ case "E": return "Ě";
+ case "L": return "Ľ";
+ case "N": return "Ň";
+ case "R": return "Ř";
+ case "S": return "Š";
+ case "T": return "Ť";
+ case "Z": return "Ž";
+ case "c": return "č";
+ case "d": return "ď";
+ case "e": return "ě";
+ case "l": return "ľ";
+ case "n": return "ň";
+ case "r": return "ř";
+ case "s": return "š";
+ case "t": return "ť";
+ case "z": return "ž";
+ }
+ return string.Empty;
+ default:
+ return encoding.GetString(buffer, index, 1);
+ }
+ }
+
+ if (header.CharacterCodeTableNumber == "01") // Latin/Cyrillic alphabet - from ISO 8859/5-1988
+ {
+ return Encoding.GetEncoding("ISO-8859-5").GetString(buffer, index, 1);
+ }
+
+ if (header.CharacterCodeTableNumber == "02") // Latin/Arabic alphabet - from ISO 8859/6-1987
+ {
+ return Encoding.GetEncoding("ISO-8859-6").GetString(buffer, index, 1);
+ }
+
+ if (header.CharacterCodeTableNumber == "03") // Latin/Greek alphabet - from ISO 8859/7-1987
+ {
+ return Encoding.GetEncoding("ISO-8859-7").GetString(buffer, index, 1); // or ISO-8859-1 ?
+ }
+
+ if (header.CharacterCodeTableNumber == "04") // Latin/Hebrew alphabet - from ISO 8859/8-1988
+ {
+ return Encoding.GetEncoding("ISO-8859-8").GetString(buffer, index, 1);
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// Read Text and Timing Information (TTI) block.
+ /// Each Text and Timing Information (TTI) block consists of 128 bytes.
+ ///
+ private IEnumerable ReadTextAndTiming(byte[] buffer, EbuGeneralSubtitleInformation header)
+ {
+ const int startOfTextAndTimingBlock = 1024;
+ const int ttiSize = 128;
+ const byte italicsOn = 0x80;
+ const byte italicsOff = 0x81;
+ const byte underlineOn = 0x82;
+ const byte underlineOff = 0x83;
+ const byte boxingOn = 0x84;
+ const byte boxingOff = 0x85;
+
+ var list = new List();
+ int index = startOfTextAndTimingBlock;
+ while (index + ttiSize <= buffer.Length)
+ {
+ var tti = new EbuTextTimingInformation
+ {
+ SubtitleGroupNumber = buffer[index],
+ SubtitleNumber = (ushort)(buffer[index + 2] * 256 + buffer[index + 1]),
+ ExtensionBlockNumber = buffer[index + 3],
+ CumulativeStatus = buffer[index + 4],
+ TimeCodeInHours = buffer[index + 5 + 0],
+ TimeCodeInMinutes = buffer[index + 5 + 1],
+ TimeCodeInSeconds = buffer[index + 5 + 2],
+ TimeCodeInMilliseconds = FramesToMillisecondsMax999(buffer[index + 5 + 3]),
+ TimeCodeOutHours = buffer[index + 9 + 0],
+ TimeCodeOutMinutes = buffer[index + 9 + 1],
+ TimeCodeOutSeconds = buffer[index + 9 + 2],
+ TimeCodeOutMilliseconds = FramesToMillisecondsMax999(buffer[index + 9 + 3]),
+ VerticalPosition = buffer[index + 13],
+ JustificationCode = buffer[index + 14],
+ CommentFlag = buffer[index + 15]
+ };
+ VerticalPositions.Add(tti.VerticalPosition);
+ JustificationCodes.Add(tti.JustificationCode);
+
+ // Text block
+ // - has a fixed length of 112 byte
+ // - 8Ah = new line
+ // - unused space = 8Fh
+ int i = index + 16; // text block start at 17th byte (index 16)
+ var open = header.DisplayStandardCode != "1" && header.DisplayStandardCode != "2";
+ var closed = header.DisplayStandardCode != "0";
+ int max = i + 112;
+ var sb = new StringBuilder();
+ while (i < max)
+ {
+ var b = buffer[i];
+ if (b <= 0x1f) // Closed - Teletext control codes
+ {
+ if (closed)
+ {
+ var tag = GetColorOrTag(b);
+ if (!string.IsNullOrEmpty(tag))
+ {
+ sb.Append(tag);
+ }
+ }
+ }
+ else if (b >= 0x20 && b <= 0x7f) // Both - Character codes
+ {
+ var ch = GetCharacter(out var skipNext, header, buffer, i);
+ sb.Append(ch);
+ if (skipNext)
+ {
+ i++;
+ }
+ }
+ else if (b >= 0x80 && b <= 0x85) // Open - italic/underline/boxing
+ {
+ if (open)
+ {
+ if (b == italicsOn && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ else if (b == italicsOff && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ else if (b == underlineOn && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ else if (b == underlineOff && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ else if (b == boxingOn && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ else if (b == boxingOff && header.LanguageCode != LanguageCodeChinese)
+ {
+ sb.Append("");
+ }
+ }
+ }
+ else if (b >= 0x86 && b <= 0x89) // Both - Reserved for future use
+ {
+ }
+ else if (b == 0x8a) // Both - CR/LF
+ {
+ sb.AppendLine();
+ }
+ else if (b >= 0x8b && b <= 0x8e) // Both - Reserved for future use
+ {
+ }
+ else if (b == 0x8f) // Both - unused space
+ {
+ }
+ else if (b >= 0x90 && b <= 0x9f) // Both - Reserved for future use
+ {
+ }
+ else if (b >= 0xa1 && b <= 0xff) // Both - Character codes
+ {
+ var ch = GetCharacter(out var skipNext, header, buffer, i);
+ sb.Append(ch);
+ if (skipNext)
+ {
+ i++;
+ }
+ }
+ i++;
+ }
+ tti.TextField = FixSpacesAndTags(sb.ToString());
+
+ if (!int.TryParse(header.MaximumNumberOfDisplayableRows, out var rows))
+ {
+ rows = 23;
+ }
+
+ if (tti.VerticalPosition < 3)
+ {
+ if (tti.JustificationCode == 1) // left
+ {
+ tti.TextField = "{\\an7}" + tti.TextField;
+ }
+ else if (tti.JustificationCode == 3) // right
+ {
+ tti.TextField = "{\\an9}" + tti.TextField;
+ }
+ else
+ {
+ tti.TextField = "{\\an8}" + tti.TextField;
+ }
+ }
+ else if (tti.VerticalPosition <= rows / 2 + 1)
+ {
+ if (tti.JustificationCode == 1) // left
+ {
+ tti.TextField = "{\\an4}" + tti.TextField;
+ }
+ else if (tti.JustificationCode == 3) // right
+ {
+ tti.TextField = "{\\an6}" + tti.TextField;
+ }
+ else
+ {
+ tti.TextField = "{\\an5}" + tti.TextField;
+ }
+ }
+ else
+ {
+ if (tti.JustificationCode == 1) // left
+ {
+ tti.TextField = "{\\an1}" + tti.TextField;
+ }
+ else if (tti.JustificationCode == 3) // right
+ {
+ tti.TextField = "{\\an3}" + tti.TextField;
+ }
+ }
+ index += ttiSize;
+ list.Add(tti);
+ }
+ return list;
+ }
+
+ private static string GetColorOrTag(byte b)
+ {
+ switch (b)
+ {
+ case 0x00:
+ return "";
+ case 0x01:
+ return "";
+ case 0x02:
+ return "";
+ case 0x03:
+ return "";
+ case 0x04:
+ return "";
+ case 0x05:
+ return "";
+ case 0x06:
+ return "";
+ case 0x07:
+ return "";
+ //case 0x0a:
+ // return "";
+ //case 0x0b:
+ // return "";
+ }
+ return null;
+ }
+
+ private static string FixSpacesAndTags(string text)
+ {
+ text = text.Trim();
+ while (text.Contains(" "))
+ {
+ text = text.Replace(" ", " ");
+ }
+
+ var match = FontTagsNoSpace1.Match(text);
+ while (match.Success)
+ {
+ text = text.Remove(match.Index, match.Length).Insert(match.Index, match.Value.Replace(" ", string.Empty).Contains("", string.Empty);
+ }
+
+ while (text.Contains(Environment.NewLine + Environment.NewLine))
+ {
+ text = text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
+ }
+
+ var lines = text.SplitToLines();
+
+ // fix multi font tags, e.g. a color in the middle of a line
+ for (var index = 0; index < lines.Count; index++)
+ {
+ var whiteTag = "";
+ var line = lines[index];
+ var changed = false;
+ var count = Utilities.CountTagInText(line, " 1)
+ {
+ count = 0;
+ var endTags = 0;
+ var idx = line.IndexOf(" 0)
+ {
+ count++;
+ var start = line.Substring(idx);
+ if (count == 1 && start.StartsWith(whiteTag))
+ {
+ line = line.Remove(idx, whiteTag.Length);
+ idx--;
+ changed = true;
+ lines[index] = line;
+ }
+ else if (count > 1 && start.StartsWith(whiteTag))
+ {
+ line = line.Remove(idx, whiteTag.Length).Insert(idx, "");
+ changed = true;
+ lines[index] = line;
+ endTags++;
+ count--;
+ }
+ else if (count > 1 && count > endTags + 1 && !start.StartsWith(whiteTag))
+ {
+ line = line.Insert(idx, "");
+ changed = true;
+ lines[index] = line;
+ idx += "".Length;
+ endTags++;
+ }
+ idx = line.IndexOf(" 0)
+ {
+ sb.Append(s);
+ if (count == 1 && !s.Contains(""))
+ {
+ sb.Append("");
+ }
+
+ sb.AppendLine();
+ }
+ }
+
+ text = sb.ToString().TrimEnd();
+
+ while (text.Contains(Environment.NewLine + " "))
+ {
+ text = text.Replace(Environment.NewLine + " ", Environment.NewLine);
+ }
+
+ // remove starting white spaces
+ match = FontTagsStartSpace.Match(text);
+ while (match.Success)
+ {
+ text = text.Remove(match.Index + match.Length - 1, 1);
+ match = FontTagsStartSpace.Match(text);
+ }
+
+ // remove starting white spaces on 2+ line
+ match = FontTagsNewLineSpace.Match(text);
+ while (match.Success)
+ {
+ text = text.Remove(match.Index + match.Length - 1, 1);
+ match = FontTagsNewLineSpace.Match(text);
+ }
+
+ text = text.Replace(" ", " ");
+
+ text = HtmlUtil.FixInvalidItalicTags(text);
+
+ return text;
+ }
+
+ public override bool IsTextBased => false;
+
+ public bool Save(string fileName, Stream stream, Subtitle subtitle, bool batchMode)
+ {
+ return Save(fileName, stream, subtitle, batchMode, null);
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/Edl.cs b/src/libse/SubtitleFormats/Edl.cs
similarity index 97%
rename from libse/SubtitleFormats/Edl.cs
rename to src/libse/SubtitleFormats/Edl.cs
index 2bd17831a..4f70b7711 100644
--- a/libse/SubtitleFormats/Edl.cs
+++ b/src/libse/SubtitleFormats/Edl.cs
@@ -1,142 +1,142 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Edl : SubtitleFormat
- {
- private static readonly Regex Regex = new Regex(@"^\d+\s+[A-Z]{2}\s+[A-Z]\s+[A-Z]\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
- private const string TextPrefix = "* FROM CLIP NAME: ";
-
- public override string Extension => ".edl";
-
- public override string Name => "EDL";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var sb = new StringBuilder();
- sb.AppendLine("TITLE: " + title);
- if (Configuration.Settings.General.CurrentFrameRate % 1.0 > 0.01)
- {
- sb.AppendLine("FCM: NON-DROP FRAME");
- }
- else
- {
- sb.AppendLine("FCM: DROP FRAME");
- }
-
- sb.AppendLine();
- const string writeFormat = "{0:000000} {1} {2} {3} {4} {5} {6} {7}";
- for (int index = 0; index < subtitle.Paragraphs.Count; index++)
- {
- int no = index + 1;
- var p = subtitle.Paragraphs[index];
- if (index == 0 && p.StartTime.TotalSeconds > 1)
- {
- var start = new TimeCode(p.StartTime.TotalMilliseconds - 1000.0);
- var end = new TimeCode(p.StartTime.TotalMilliseconds - 1);
- sb.AppendLine(string.Format(writeFormat, no, "BL", "V", "C", EncodeTimeCode(start), EncodeTimeCode(end), EncodeTimeCode(start), EncodeTimeCode(end)));
- sb.AppendLine();
- }
- var text = HtmlUtil.RemoveHtmlTags(p.Text, true);
- sb.AppendLine(string.Format(writeFormat, no, "AX", "V", "C", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime)));
- sb.AppendLine(TextPrefix + text);
- sb.AppendLine();
- var next = subtitle.GetParagraphOrDefault(no);
- if (next != null && next.StartTime.TotalMilliseconds > p.EndTime.TotalMilliseconds + 100)
- {
- var start = new TimeCode(p.EndTime.TotalMilliseconds + 1);
- var end = new TimeCode(start.TotalMilliseconds + 1000);
- if (end.TotalMilliseconds >= next.StartTime.TotalMilliseconds)
- {
- end = new TimeCode(next.StartTime.TotalMilliseconds - 1);
- }
- sb.AppendLine(string.Format(writeFormat, no, "BL", "V", "C", EncodeTimeCode(start), EncodeTimeCode(end), EncodeTimeCode(start), EncodeTimeCode(end)));
- sb.AppendLine();
- }
- }
- return sb.ToString().Trim() + Environment.NewLine;
- }
-
- private static string EncodeTimeCode(TimeCode timeCode)
- {
- return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- { //002 AX V C 01:00:01:15 01:00:04:18 00:00:01:15 00:00:04:18
- //000002 AX V C 01:00:04:00 01:00:05:00 00:00:02:05 00:00:03:05
- _errorCount = 0;
- Paragraph lastParagraph = null;
- int count = 0;
- var splitChar = new[] { ' ' };
- foreach (string line in lines)
- {
- bool isTimeCode = false;
- if (line.Length > 0)
- {
- bool success = false;
- if (line.Length > 65 && line.Length < 85 && line.IndexOf(':') > 20)
- {
- var match = Regex.Match(line);
- if (match.Success)
- {
- isTimeCode = true;
- if (lastParagraph != null && Math.Abs(lastParagraph.StartTime.TotalMilliseconds + 1) > 0.001)
- {
- subtitle.Paragraphs.Add(lastParagraph);
- }
-
- var arr = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
- try
- {
- if (arr.Length == 8 && arr[1] != "BL")
- {
- var start = DecodeTimeCodeFrames(arr[6], SplitCharColon);
- var end = DecodeTimeCodeFrames(arr[7], SplitCharColon);
- lastParagraph = new Paragraph(start, end, string.Empty);
- success = true;
- }
- else
- {
- lastParagraph = new Paragraph(string.Empty, -1, -1);
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- if (!isTimeCode && !string.IsNullOrWhiteSpace(line) && lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5)
- {
- lastParagraph.Text = (lastParagraph.Text + Environment.NewLine + line).Trim();
- success = true;
- }
- if (!success && count > 9)
- {
- _errorCount++;
- }
- }
- count++;
- }
- if (lastParagraph != null)
- {
- subtitle.Paragraphs.Add(lastParagraph);
- }
- foreach (var paragraph in subtitle.Paragraphs)
- {
- if (paragraph.Text.StartsWith(TextPrefix, StringComparison.Ordinal))
- {
- paragraph.Text = paragraph.Text.Remove(0, TextPrefix.Length).TrimStart();
- }
- }
-
- subtitle.Renumber();
- }
-
- }
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Edl : SubtitleFormat
+ {
+ private static readonly Regex Regex = new Regex(@"^\d+\s+[A-Z]{2}\s+[A-Z]\s+[A-Z]\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d\s+\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
+ private const string TextPrefix = "* FROM CLIP NAME: ";
+
+ public override string Extension => ".edl";
+
+ public override string Name => "EDL";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("TITLE: " + title);
+ if (Configuration.Settings.General.CurrentFrameRate % 1.0 > 0.01)
+ {
+ sb.AppendLine("FCM: NON-DROP FRAME");
+ }
+ else
+ {
+ sb.AppendLine("FCM: DROP FRAME");
+ }
+
+ sb.AppendLine();
+ const string writeFormat = "{0:000000} {1} {2} {3} {4} {5} {6} {7}";
+ for (int index = 0; index < subtitle.Paragraphs.Count; index++)
+ {
+ int no = index + 1;
+ var p = subtitle.Paragraphs[index];
+ if (index == 0 && p.StartTime.TotalSeconds > 1)
+ {
+ var start = new TimeCode(p.StartTime.TotalMilliseconds - 1000.0);
+ var end = new TimeCode(p.StartTime.TotalMilliseconds - 1);
+ sb.AppendLine(string.Format(writeFormat, no, "BL", "V", "C", EncodeTimeCode(start), EncodeTimeCode(end), EncodeTimeCode(start), EncodeTimeCode(end)));
+ sb.AppendLine();
+ }
+ var text = HtmlUtil.RemoveHtmlTags(p.Text, true);
+ sb.AppendLine(string.Format(writeFormat, no, "AX", "V", "C", EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime)));
+ sb.AppendLine(TextPrefix + text);
+ sb.AppendLine();
+ var next = subtitle.GetParagraphOrDefault(no);
+ if (next != null && next.StartTime.TotalMilliseconds > p.EndTime.TotalMilliseconds + 100)
+ {
+ var start = new TimeCode(p.EndTime.TotalMilliseconds + 1);
+ var end = new TimeCode(start.TotalMilliseconds + 1000);
+ if (end.TotalMilliseconds >= next.StartTime.TotalMilliseconds)
+ {
+ end = new TimeCode(next.StartTime.TotalMilliseconds - 1);
+ }
+ sb.AppendLine(string.Format(writeFormat, no, "BL", "V", "C", EncodeTimeCode(start), EncodeTimeCode(end), EncodeTimeCode(start), EncodeTimeCode(end)));
+ sb.AppendLine();
+ }
+ }
+ return sb.ToString().Trim() + Environment.NewLine;
+ }
+
+ private static string EncodeTimeCode(TimeCode timeCode)
+ {
+ return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ { //002 AX V C 01:00:01:15 01:00:04:18 00:00:01:15 00:00:04:18
+ //000002 AX V C 01:00:04:00 01:00:05:00 00:00:02:05 00:00:03:05
+ _errorCount = 0;
+ Paragraph lastParagraph = null;
+ int count = 0;
+ var splitChar = new[] { ' ' };
+ foreach (string line in lines)
+ {
+ bool isTimeCode = false;
+ if (line.Length > 0)
+ {
+ bool success = false;
+ if (line.Length > 65 && line.Length < 85 && line.IndexOf(':') > 20)
+ {
+ var match = Regex.Match(line);
+ if (match.Success)
+ {
+ isTimeCode = true;
+ if (lastParagraph != null && Math.Abs(lastParagraph.StartTime.TotalMilliseconds + 1) > 0.001)
+ {
+ subtitle.Paragraphs.Add(lastParagraph);
+ }
+
+ var arr = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
+ try
+ {
+ if (arr.Length == 8 && arr[1] != "BL")
+ {
+ var start = DecodeTimeCodeFrames(arr[6], SplitCharColon);
+ var end = DecodeTimeCodeFrames(arr[7], SplitCharColon);
+ lastParagraph = new Paragraph(start, end, string.Empty);
+ success = true;
+ }
+ else
+ {
+ lastParagraph = new Paragraph(string.Empty, -1, -1);
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ if (!isTimeCode && !string.IsNullOrWhiteSpace(line) && lastParagraph != null && Utilities.GetNumberOfLines(lastParagraph.Text) < 5)
+ {
+ lastParagraph.Text = (lastParagraph.Text + Environment.NewLine + line).Trim();
+ success = true;
+ }
+ if (!success && count > 9)
+ {
+ _errorCount++;
+ }
+ }
+ count++;
+ }
+ if (lastParagraph != null)
+ {
+ subtitle.Paragraphs.Add(lastParagraph);
+ }
+ foreach (var paragraph in subtitle.Paragraphs)
+ {
+ if (paragraph.Text.StartsWith(TextPrefix, StringComparison.Ordinal))
+ {
+ paragraph.Text = paragraph.Text.Remove(0, TextPrefix.Length).TrimStart();
+ }
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
}
\ No newline at end of file
diff --git a/libse/SubtitleFormats/Eeg708.cs b/src/libse/SubtitleFormats/Eeg708.cs
similarity index 97%
rename from libse/SubtitleFormats/Eeg708.cs
rename to src/libse/SubtitleFormats/Eeg708.cs
index 9ba402f77..c6fbeaa2b 100644
--- a/libse/SubtitleFormats/Eeg708.cs
+++ b/src/libse/SubtitleFormats/Eeg708.cs
@@ -1,105 +1,105 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Eeg708 : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "EEG 708";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("Caption");
- XmlAttribute start = xml.CreateAttribute("timecode");
- start.InnerText = EncodeTimeCode(p.StartTime);
- paragraph.Attributes.Append(start);
- XmlNode text = xml.CreateElement("Text");
- text.InnerText = p.Text;
- paragraph.AppendChild(text);
- xml.DocumentElement.AppendChild(paragraph);
-
- paragraph = xml.CreateElement("Caption");
- start = xml.CreateAttribute("timecode");
- start.InnerText = EncodeTimeCode(p.EndTime);
- paragraph.Attributes.Append(start);
- xml.DocumentElement.AppendChild(paragraph);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string allText = sb.ToString();
- if (!allText.Contains("", Environment.NewLine).Replace("
", Environment.NewLine);
- TimeCode startTime = DecodeTimeCodeFramesFourParts(start.Split(':'));
- lastParagraph = new Paragraph(s, startTime.TotalMilliseconds, startTime.TotalMilliseconds + 3000);
- subtitle.Paragraphs.Add(lastParagraph);
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Eeg708 : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "EEG 708";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("Caption");
+ XmlAttribute start = xml.CreateAttribute("timecode");
+ start.InnerText = EncodeTimeCode(p.StartTime);
+ paragraph.Attributes.Append(start);
+ XmlNode text = xml.CreateElement("Text");
+ text.InnerText = p.Text;
+ paragraph.AppendChild(text);
+ xml.DocumentElement.AppendChild(paragraph);
+
+ paragraph = xml.CreateElement("Caption");
+ start = xml.CreateAttribute("timecode");
+ start.InnerText = EncodeTimeCode(p.EndTime);
+ paragraph.Attributes.Append(start);
+ xml.DocumentElement.AppendChild(paragraph);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string allText = sb.ToString();
+ if (!allText.Contains("", Environment.NewLine).Replace("
", Environment.NewLine);
+ TimeCode startTime = DecodeTimeCodeFramesFourParts(start.Split(':'));
+ lastParagraph = new Paragraph(s, startTime.TotalMilliseconds, startTime.TotalMilliseconds + 3000);
+ subtitle.Paragraphs.Add(lastParagraph);
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/ElrPrint.cs b/src/libse/SubtitleFormats/ElrPrint.cs
similarity index 100%
rename from libse/SubtitleFormats/ElrPrint.cs
rename to src/libse/SubtitleFormats/ElrPrint.cs
diff --git a/libse/SubtitleFormats/F4Rtf.cs b/src/libse/SubtitleFormats/F4Rtf.cs
similarity index 96%
rename from libse/SubtitleFormats/F4Rtf.cs
rename to src/libse/SubtitleFormats/F4Rtf.cs
index 14791a08d..0ef584711 100644
--- a/libse/SubtitleFormats/F4Rtf.cs
+++ b/src/libse/SubtitleFormats/F4Rtf.cs
@@ -1,48 +1,48 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class F4Rtf : F4Text
- {
- public override string Extension => ".rtf";
-
- public override string Name => "F4 Rich Text Format";
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return ToF4Text(subtitle).ToRtf();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- foreach (string line in lines)
- {
- sb.AppendLine(line);
- }
-
- string rtf = sb.ToString().Trim();
- if (!rtf.StartsWith("{\\rtf", StringComparison.Ordinal))
- {
- return;
- }
-
- LoadF4TextSubtitle(subtitle, rtf.FromRtf());
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class F4Rtf : F4Text
+ {
+ public override string Extension => ".rtf";
+
+ public override string Name => "F4 Rich Text Format";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return ToF4Text(subtitle).ToRtf();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ foreach (string line in lines)
+ {
+ sb.AppendLine(line);
+ }
+
+ string rtf = sb.ToString().Trim();
+ if (!rtf.StartsWith("{\\rtf", StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ LoadF4TextSubtitle(subtitle, rtf.FromRtf());
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/F4Text.cs b/src/libse/SubtitleFormats/F4Text.cs
similarity index 97%
rename from libse/SubtitleFormats/F4Text.cs
rename to src/libse/SubtitleFormats/F4Text.cs
index e09f54fa8..30287ca53 100644
--- a/libse/SubtitleFormats/F4Text.cs
+++ b/src/libse/SubtitleFormats/F4Text.cs
@@ -1,156 +1,156 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- ///
- /// #00:00:06-8#
- /// http://www.audiotranskription.de
- ///
- public class F4Text : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d-\d$", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "F4 Text";
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public static string ToF4Text(Subtitle subtitle)
- {
- var sb = new StringBuilder();
- const string writeFormat = "{0}{1}";
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- sb.AppendFormat(writeFormat, HtmlUtil.RemoveHtmlTags(p.Text, true), EncodeTimeCode(p.EndTime));
- }
- return sb.ToString().Trim();
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- return ToF4Text(subtitle);
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- return $" #{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}-{Math.Round(time.Milliseconds / 100.0, 0):0}# ";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- foreach (string line in lines)
- {
- sb.AppendLine(line);
- }
-
- string text = sb.ToString();
- if (text.Contains("{\\rtf") || text.Contains(""))
- {
- return;
- }
-
- LoadF4TextSubtitle(subtitle, text);
- }
-
- protected void LoadF4TextSubtitle(Subtitle subtitle, string text)
- {
- Paragraph p = null;
- subtitle.Paragraphs.Clear();
- var arr = text.Trim().Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
- var currentText = new StringBuilder();
- foreach (string line in arr)
- {
- if (RegexTimeCodes.IsMatch(line))
- {
- if (p == null)
- {
- p = new Paragraph();
- if (currentText.Length > 0)
- {
- p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
- p.Text = p.Text.Trim('\n', '\r');
- subtitle.Paragraphs.Add(p);
- p = new Paragraph();
- }
- }
- if (Math.Abs(p.StartTime.TotalMilliseconds) < 0.01 || currentText.Length == 0)
- {
- p.StartTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries));
- }
- else
- {
- p.EndTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries));
- p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
- p.Text = p.Text.Trim('\n', '\r').Trim();
- subtitle.Paragraphs.Add(p);
- p = null;
- currentText.Clear();
- }
- }
- else
- {
- if (p == null && subtitle.Paragraphs.Count > 0)
- {
- p = new Paragraph { StartTime = { TotalMilliseconds = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalMilliseconds } };
- }
- currentText.AppendLine(line.Trim());
- }
- }
- if (currentText.Length > 0 && subtitle.Paragraphs.Count > 0 && currentText.Length < 1000)
- {
- if (p == null)
- {
- p = new Paragraph();
- }
-
- p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
- p.Text = p.Text.Trim('\n', '\r').Trim();
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 3000;
- subtitle.Paragraphs.Add(p);
- }
- subtitle.Renumber();
- }
-
- private TimeCode DecodeTimeCode(string[] parts)
- {
- var tc = new TimeCode();
- try
- {
- int hour = int.Parse(parts[0]);
- int minutes = int.Parse(parts[1]);
- int seconds = int.Parse(parts[2]);
- int millisecond = int.Parse(parts[3]);
-
- int milliseconds = (int)Math.Round(millisecond * 100.0);
- if (milliseconds > 999)
- {
- milliseconds = 999;
- }
-
- tc = new TimeCode(hour, minutes, seconds, milliseconds);
- }
- catch (Exception exception)
- {
- System.Diagnostics.Debug.WriteLine(exception.Message);
- _errorCount++;
- }
- return tc;
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ ///
+ /// #00:00:06-8#
+ /// http://www.audiotranskription.de
+ ///
+ public class F4Text : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d-\d$", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "F4 Text";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public static string ToF4Text(Subtitle subtitle)
+ {
+ var sb = new StringBuilder();
+ const string writeFormat = "{0}{1}";
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ sb.AppendFormat(writeFormat, HtmlUtil.RemoveHtmlTags(p.Text, true), EncodeTimeCode(p.EndTime));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ return ToF4Text(subtitle);
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ return $" #{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}-{Math.Round(time.Milliseconds / 100.0, 0):0}# ";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ foreach (string line in lines)
+ {
+ sb.AppendLine(line);
+ }
+
+ string text = sb.ToString();
+ if (text.Contains("{\\rtf") || text.Contains(""))
+ {
+ return;
+ }
+
+ LoadF4TextSubtitle(subtitle, text);
+ }
+
+ protected void LoadF4TextSubtitle(Subtitle subtitle, string text)
+ {
+ Paragraph p = null;
+ subtitle.Paragraphs.Clear();
+ var arr = text.Trim().Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
+ var currentText = new StringBuilder();
+ foreach (string line in arr)
+ {
+ if (RegexTimeCodes.IsMatch(line))
+ {
+ if (p == null)
+ {
+ p = new Paragraph();
+ if (currentText.Length > 0)
+ {
+ p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
+ p.Text = p.Text.Trim('\n', '\r');
+ subtitle.Paragraphs.Add(p);
+ p = new Paragraph();
+ }
+ }
+ if (Math.Abs(p.StartTime.TotalMilliseconds) < 0.01 || currentText.Length == 0)
+ {
+ p.StartTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries));
+ }
+ else
+ {
+ p.EndTime = DecodeTimeCode(line.Split(new[] { ':', '-' }, StringSplitOptions.RemoveEmptyEntries));
+ p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
+ p.Text = p.Text.Trim('\n', '\r').Trim();
+ subtitle.Paragraphs.Add(p);
+ p = null;
+ currentText.Clear();
+ }
+ }
+ else
+ {
+ if (p == null && subtitle.Paragraphs.Count > 0)
+ {
+ p = new Paragraph { StartTime = { TotalMilliseconds = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalMilliseconds } };
+ }
+ currentText.AppendLine(line.Trim());
+ }
+ }
+ if (currentText.Length > 0 && subtitle.Paragraphs.Count > 0 && currentText.Length < 1000)
+ {
+ if (p == null)
+ {
+ p = new Paragraph();
+ }
+
+ p.Text = currentText.ToString().Trim().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
+ p.Text = p.Text.Trim('\n', '\r').Trim();
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + 3000;
+ subtitle.Paragraphs.Add(p);
+ }
+ subtitle.Renumber();
+ }
+
+ private TimeCode DecodeTimeCode(string[] parts)
+ {
+ var tc = new TimeCode();
+ try
+ {
+ int hour = int.Parse(parts[0]);
+ int minutes = int.Parse(parts[1]);
+ int seconds = int.Parse(parts[2]);
+ int millisecond = int.Parse(parts[3]);
+
+ int milliseconds = (int)Math.Round(millisecond * 100.0);
+ if (milliseconds > 999)
+ {
+ milliseconds = 999;
+ }
+
+ tc = new TimeCode(hour, minutes, seconds, milliseconds);
+ }
+ catch (Exception exception)
+ {
+ System.Diagnostics.Debug.WriteLine(exception.Message);
+ _errorCount++;
+ }
+ return tc;
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/F4Xml.cs b/src/libse/SubtitleFormats/F4Xml.cs
similarity index 96%
rename from libse/SubtitleFormats/F4Xml.cs
rename to src/libse/SubtitleFormats/F4Xml.cs
index 6f712dd9f..c8b4c460a 100644
--- a/libse/SubtitleFormats/F4Xml.cs
+++ b/src/libse/SubtitleFormats/F4Xml.cs
@@ -1,74 +1,74 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class F4Xml : F4Text
- {
- public override string Extension => ".xml";
-
- public override string Name => "F4 Xml";
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var xml = new XmlDocument();
- var template = @"
-
-
-
-
-".Replace("'", "\"");
- xml.LoadXml(template);
- xml.DocumentElement.SelectSingleNode("content").Attributes["content"].Value = ToF4Text(subtitle);
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- foreach (string line in lines)
- {
- sb.AppendLine(line);
- }
-
- string xml = sb.ToString();
- if (!xml.Contains(" ".xml";
+
+ public override string Name => "F4 Xml";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var xml = new XmlDocument();
+ var template = @"
+
+
+
+
+".Replace("'", "\"");
+ xml.LoadXml(template);
+ xml.DocumentElement.SelectSingleNode("content").Attributes["content"].Value = ToF4Text(subtitle);
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ foreach (string line in lines)
+ {
+ sb.AppendLine(line);
+ }
+
+ string xml = sb.ToString();
+ if (!xml.Contains(" ".xml";
-
- public override string Name => "FLVCoreCuePoints";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("CuePoint");
-
- XmlNode startTime = xml.CreateElement("Time");
- startTime.InnerText = p.StartTime.TotalMilliseconds.ToString();
- paragraph.AppendChild(startTime);
-
- XmlNode paragraphType = xml.CreateElement("Type");
- paragraphType.InnerText = "event";
- paragraph.AppendChild(paragraphType);
-
- XmlNode name = xml.CreateElement("Name");
- name.InnerText = p.Text;
- paragraph.AppendChild(name);
-
- XmlNode parameters = xml.CreateElement("Parameters");
-
- XmlNode parameter = xml.CreateElement("Parameter");
- name = xml.CreateElement("Name");
- name.InnerText = "source";
- XmlNode value = xml.CreateElement("Value");
- value.InnerText = "transcription";
- parameter.AppendChild(name);
- parameter.AppendChild(value);
- parameters.AppendChild(parameter);
-
- parameter = xml.CreateElement("Parameter");
- name = xml.CreateElement("Name");
- name.InnerText = "duration";
- value = xml.CreateElement("Value");
- value.InnerText = p.Duration.TotalMilliseconds.ToString();
- parameter.AppendChild(name);
- parameter.AppendChild(value);
- parameters.AppendChild(parameter);
-
- parameter = xml.CreateElement("Parameter");
- name = xml.CreateElement("Name");
- name.InnerText = "confidence";
- value = xml.CreateElement("Value");
- value.InnerText = "50";
- parameter.AppendChild(name);
- parameter.AppendChild(value);
- parameters.AppendChild(parameter);
-
- paragraph.AppendChild(parameters);
-
- xml.DocumentElement.AppendChild(paragraph);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
-
- string allText = sb.ToString();
- if (!allText.Contains(" next.StartTime.TotalMilliseconds)
- {
- p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
- }
- }
- }
-
- subtitle.Renumber();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FLVCoreCuePoints : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "FLVCoreCuePoints";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("CuePoint");
+
+ XmlNode startTime = xml.CreateElement("Time");
+ startTime.InnerText = p.StartTime.TotalMilliseconds.ToString();
+ paragraph.AppendChild(startTime);
+
+ XmlNode paragraphType = xml.CreateElement("Type");
+ paragraphType.InnerText = "event";
+ paragraph.AppendChild(paragraphType);
+
+ XmlNode name = xml.CreateElement("Name");
+ name.InnerText = p.Text;
+ paragraph.AppendChild(name);
+
+ XmlNode parameters = xml.CreateElement("Parameters");
+
+ XmlNode parameter = xml.CreateElement("Parameter");
+ name = xml.CreateElement("Name");
+ name.InnerText = "source";
+ XmlNode value = xml.CreateElement("Value");
+ value.InnerText = "transcription";
+ parameter.AppendChild(name);
+ parameter.AppendChild(value);
+ parameters.AppendChild(parameter);
+
+ parameter = xml.CreateElement("Parameter");
+ name = xml.CreateElement("Name");
+ name.InnerText = "duration";
+ value = xml.CreateElement("Value");
+ value.InnerText = p.Duration.TotalMilliseconds.ToString();
+ parameter.AppendChild(name);
+ parameter.AppendChild(value);
+ parameters.AppendChild(parameter);
+
+ parameter = xml.CreateElement("Parameter");
+ name = xml.CreateElement("Name");
+ name.InnerText = "confidence";
+ value = xml.CreateElement("Value");
+ value.InnerText = "50";
+ parameter.AppendChild(name);
+ parameter.AppendChild(value);
+ parameters.AppendChild(parameter);
+
+ paragraph.AppendChild(parameters);
+
+ xml.DocumentElement.AppendChild(paragraph);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+
+ string allText = sb.ToString();
+ if (!allText.Contains(" next.StartTime.TotalMilliseconds)
+ {
+ p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines;
+ }
+ }
+ }
+
+ subtitle.Renumber();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FabSubtitler.cs b/src/libse/SubtitleFormats/FabSubtitler.cs
similarity index 97%
rename from libse/SubtitleFormats/FabSubtitler.cs
rename to src/libse/SubtitleFormats/FabSubtitler.cs
index 8a939e7aa..e9094e524 100644
--- a/libse/SubtitleFormats/FabSubtitler.cs
+++ b/src/libse/SubtitleFormats/FabSubtitler.cs
@@ -1,80 +1,80 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FabSubtitler : SubtitleFormat
- {
- private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
-
- public override string Extension => ".txt";
-
- public override string Name => "FAB Subtitler";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- var sb = new StringBuilder();
- const string writeFormat = "{0} {1}{2}{3}{2}";
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- //00:50:34:22 00:50:39:13
- //Ich muss dafür sorgen,
- //dass die Epsteins weiterleben
- sb.AppendLine(string.Format(writeFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text)));
- }
- return sb.ToString();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- //00:50:39:13 (last is frame)
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- //00:03:15:22 00:03:23:10 This is line one.
- //This is line two.
- Paragraph p = null;
- subtitle.Paragraphs.Clear();
- _errorCount = 0;
- foreach (string line in lines)
- {
- if (RegexTimeCodes.IsMatch(line))
- {
- string temp = line.Substring(0, RegexTimeCodes.Match(line).Length);
- string start = temp.Substring(0, 11);
- string end = temp.Substring(13, 11);
-
- string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
- if (startParts.Length == 4 && endParts.Length == 4)
- {
- p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), string.Empty);
- subtitle.Paragraphs.Add(p);
- }
- }
- else if (string.IsNullOrWhiteSpace(line))
- {
- // skip these lines
- }
- else if (p != null)
- {
- if (string.IsNullOrEmpty(p.Text))
- {
- p.Text = line;
- }
- else
- {
- p.Text = p.Text + Environment.NewLine + line;
- }
- }
- }
-
- subtitle.Renumber();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FabSubtitler : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d \d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled);
+
+ public override string Extension => ".txt";
+
+ public override string Name => "FAB Subtitler";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var sb = new StringBuilder();
+ const string writeFormat = "{0} {1}{2}{3}{2}";
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ //00:50:34:22 00:50:39:13
+ //Ich muss dafür sorgen,
+ //dass die Epsteins weiterleben
+ sb.AppendLine(string.Format(writeFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), Environment.NewLine, HtmlUtil.RemoveHtmlTags(p.Text)));
+ }
+ return sb.ToString();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ //00:50:39:13 (last is frame)
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}:{MillisecondsToFramesMaxFrameRate(time.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ //00:03:15:22 00:03:23:10 This is line one.
+ //This is line two.
+ Paragraph p = null;
+ subtitle.Paragraphs.Clear();
+ _errorCount = 0;
+ foreach (string line in lines)
+ {
+ if (RegexTimeCodes.IsMatch(line))
+ {
+ string temp = line.Substring(0, RegexTimeCodes.Match(line).Length);
+ string start = temp.Substring(0, 11);
+ string end = temp.Substring(13, 11);
+
+ string[] startParts = start.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ string[] endParts = end.Split(SplitCharColon, StringSplitOptions.RemoveEmptyEntries);
+ if (startParts.Length == 4 && endParts.Length == 4)
+ {
+ p = new Paragraph(DecodeTimeCodeFramesFourParts(startParts), DecodeTimeCodeFramesFourParts(endParts), string.Empty);
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ else if (string.IsNullOrWhiteSpace(line))
+ {
+ // skip these lines
+ }
+ else if (p != null)
+ {
+ if (string.IsNullOrEmpty(p.Text))
+ {
+ p.Text = line;
+ }
+ else
+ {
+ p.Text = p.Text + Environment.NewLine + line;
+ }
+ }
+ }
+
+ subtitle.Renumber();
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/FilmEditXml.cs b/src/libse/SubtitleFormats/FilmEditXml.cs
similarity index 97%
rename from libse/SubtitleFormats/FilmEditXml.cs
rename to src/libse/SubtitleFormats/FilmEditXml.cs
index 35355e786..88eeb8d1b 100644
--- a/libse/SubtitleFormats/FilmEditXml.cs
+++ b/src/libse/SubtitleFormats/FilmEditXml.cs
@@ -1,155 +1,155 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FilmEditXml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Film Edit xml";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string xmlAsString = sb.ToString().Trim();
- if (xmlAsString.Contains("") && xmlAsString.Contains(""))
- {
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(xmlAsString);
- var paragraphs = xml.DocumentElement.SelectNodes("subtitle");
- return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "filmeditxml";
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- }
- }
- return false;
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "Arial" + Environment.NewLine +
- "22" + Environment.NewLine +
- "720" + Environment.NewLine +
- "576" + Environment.NewLine +
- "586" + Environment.NewLine +
- "330" + Environment.NewLine +
- "1420" + Environment.NewLine +
- "25" + Environment.NewLine +
- "False" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- XmlNode div = xml.DocumentElement;
- int no = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("subtitle");
- string text = HtmlUtil.RemoveHtmlTags(p.Text);
-
- XmlNode num = xml.CreateElement("num");
- num.InnerText = no.ToString();
- paragraph.AppendChild(num);
-
- XmlNode dur = xml.CreateElement("dur");
- dur.InnerText = EncodeDuration(p.Duration);
- paragraph.AppendChild(dur);
-
- XmlNode textNode = xml.CreateElement("text");
- textNode.InnerText = text.Replace(Environment.NewLine, "\\N");
- paragraph.AppendChild(textNode);
-
- XmlNode timeIn = xml.CreateElement("in");
- timeIn.InnerText = EncodeTimeCode(p.StartTime);
- paragraph.AppendChild(timeIn);
-
- XmlNode timeOut = xml.CreateElement("out");
- timeOut.InnerText = EncodeTimeCode(p.EndTime);
- paragraph.AppendChild(timeOut);
-
- XmlNode align = xml.CreateElement("align");
- align.InnerText = "C";
- paragraph.AppendChild(align);
-
- XmlNode posx = xml.CreateElement("posx");
- posx.InnerText = "0";
- paragraph.AppendChild(posx);
-
- XmlNode post = xml.CreateElement("posy");
- post.InnerText = "308";
- paragraph.AppendChild(post);
-
- XmlNode memo = xml.CreateElement("memo");
- paragraph.AppendChild(memo);
-
- div.AppendChild(paragraph);
- no++;
- }
-
- return ToUtf8XmlString(xml);
- }
-
- private static string EncodeDuration(TimeCode timeCode)
- {
- return $"{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
- }
-
- private static string EncodeTimeCode(TimeCode timeCode)
- {
- return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(sb.ToString().Trim());
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("subtitle"))
- {
- try
- {
- var p = new Paragraph();
- foreach (XmlNode innerNode in node.ChildNodes)
- {
- switch (innerNode.Name)
- {
- case "text":
- p.Text = innerNode.InnerText.Replace("\\N", Environment.NewLine);
- break;
- case "in":
- p.StartTime = DecodeTimeCodeFrames(innerNode.InnerText, SplitCharColon);
- break;
- case "out":
- p.EndTime = DecodeTimeCodeFrames(innerNode.InnerText, SplitCharColon);
- break;
- }
- }
- if (p.StartTime.TotalSeconds >= 0 && p.EndTime.TotalMilliseconds > 0 && !string.IsNullOrEmpty(p.Text))
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FilmEditXml : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Film Edit xml";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string xmlAsString = sb.ToString().Trim();
+ if (xmlAsString.Contains("") && xmlAsString.Contains(""))
+ {
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(xmlAsString);
+ var paragraphs = xml.DocumentElement.SelectNodes("subtitle");
+ return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "filmeditxml";
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ }
+ }
+ return false;
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "Arial" + Environment.NewLine +
+ "22" + Environment.NewLine +
+ "720" + Environment.NewLine +
+ "576" + Environment.NewLine +
+ "586" + Environment.NewLine +
+ "330" + Environment.NewLine +
+ "1420" + Environment.NewLine +
+ "25" + Environment.NewLine +
+ "False" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ XmlNode div = xml.DocumentElement;
+ int no = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("subtitle");
+ string text = HtmlUtil.RemoveHtmlTags(p.Text);
+
+ XmlNode num = xml.CreateElement("num");
+ num.InnerText = no.ToString();
+ paragraph.AppendChild(num);
+
+ XmlNode dur = xml.CreateElement("dur");
+ dur.InnerText = EncodeDuration(p.Duration);
+ paragraph.AppendChild(dur);
+
+ XmlNode textNode = xml.CreateElement("text");
+ textNode.InnerText = text.Replace(Environment.NewLine, "\\N");
+ paragraph.AppendChild(textNode);
+
+ XmlNode timeIn = xml.CreateElement("in");
+ timeIn.InnerText = EncodeTimeCode(p.StartTime);
+ paragraph.AppendChild(timeIn);
+
+ XmlNode timeOut = xml.CreateElement("out");
+ timeOut.InnerText = EncodeTimeCode(p.EndTime);
+ paragraph.AppendChild(timeOut);
+
+ XmlNode align = xml.CreateElement("align");
+ align.InnerText = "C";
+ paragraph.AppendChild(align);
+
+ XmlNode posx = xml.CreateElement("posx");
+ posx.InnerText = "0";
+ paragraph.AppendChild(posx);
+
+ XmlNode post = xml.CreateElement("posy");
+ post.InnerText = "308";
+ paragraph.AppendChild(post);
+
+ XmlNode memo = xml.CreateElement("memo");
+ paragraph.AppendChild(memo);
+
+ div.AppendChild(paragraph);
+ no++;
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ private static string EncodeDuration(TimeCode timeCode)
+ {
+ return $"{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
+ }
+
+ private static string EncodeTimeCode(TimeCode timeCode)
+ {
+ return $"{timeCode.Hours:00}:{timeCode.Minutes:00}:{timeCode.Seconds:00}:{MillisecondsToFramesMaxFrameRate(timeCode.Milliseconds):00}";
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(sb.ToString().Trim());
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("subtitle"))
+ {
+ try
+ {
+ var p = new Paragraph();
+ foreach (XmlNode innerNode in node.ChildNodes)
+ {
+ switch (innerNode.Name)
+ {
+ case "text":
+ p.Text = innerNode.InnerText.Replace("\\N", Environment.NewLine);
+ break;
+ case "in":
+ p.StartTime = DecodeTimeCodeFrames(innerNode.InnerText, SplitCharColon);
+ break;
+ case "out":
+ p.EndTime = DecodeTimeCodeFrames(innerNode.InnerText, SplitCharColon);
+ break;
+ }
+ }
+ if (p.StartTime.TotalSeconds >= 0 && p.EndTime.TotalMilliseconds > 0 && !string.IsNullOrEmpty(p.Text))
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProImage.cs b/src/libse/SubtitleFormats/FinalCutProImage.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProImage.cs
rename to src/libse/SubtitleFormats/FinalCutProImage.cs
index c36999fae..3715e825c 100644
--- a/libse/SubtitleFormats/FinalCutProImage.cs
+++ b/src/libse/SubtitleFormats/FinalCutProImage.cs
@@ -1,107 +1,107 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProImage : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".xml";
-
- public override string Name => "Final Cut Pro Image";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- throw new NotImplementedException();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
- {
- try
- {
- var frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
- if (frameRate > 10 && frameRate < 2000)
- {
- Configuration.Settings.General.CurrentFrameRate = frameRate;
- }
- }
- catch
- {
- // ignored
- }
- }
-
-
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("sequence/media/video/track/clipitem"))
- {
- try
- {
- XmlNode fileNode = node.SelectSingleNode("file");
- if (fileNode != null)
- {
- XmlNode fileNameNode = fileNode.SelectSingleNode("name");
- XmlNode pathurlNode = fileNode.SelectSingleNode("pathurl");
- if (fileNameNode != null)
- {
- var p = new Paragraph();
- p.Text = fileNameNode.InnerText;
- if (pathurlNode != null)
- {
- p.Extra = pathurlNode.InnerText;
- }
-
- XmlNode inNode = node.SelectSingleNode("in");
- XmlNode startNode = node.SelectSingleNode("start");
- if (startNode != null)
- {
- p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(startNode.InnerText));
- }
- else if (inNode != null)
- {
- p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(inNode.InnerText));
- }
- XmlNode outNode = node.SelectSingleNode("out");
- XmlNode endNode = node.SelectSingleNode("end");
- if (endNode != null)
- {
- p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(endNode.InnerText));
- }
- else if (outNode != null)
- {
- p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(outNode.InnerText));
- }
- subtitle.Paragraphs.Add(p);
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProImage : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".xml";
+
+ public override string Name => "Final Cut Pro Image";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
+ {
+ try
+ {
+ var frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
+ if (frameRate > 10 && frameRate < 2000)
+ {
+ Configuration.Settings.General.CurrentFrameRate = frameRate;
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("sequence/media/video/track/clipitem"))
+ {
+ try
+ {
+ XmlNode fileNode = node.SelectSingleNode("file");
+ if (fileNode != null)
+ {
+ XmlNode fileNameNode = fileNode.SelectSingleNode("name");
+ XmlNode pathurlNode = fileNode.SelectSingleNode("pathurl");
+ if (fileNameNode != null)
+ {
+ var p = new Paragraph();
+ p.Text = fileNameNode.InnerText;
+ if (pathurlNode != null)
+ {
+ p.Extra = pathurlNode.InnerText;
+ }
+
+ XmlNode inNode = node.SelectSingleNode("in");
+ XmlNode startNode = node.SelectSingleNode("start");
+ if (startNode != null)
+ {
+ p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(startNode.InnerText));
+ }
+ else if (inNode != null)
+ {
+ p.StartTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(inNode.InnerText));
+ }
+ XmlNode outNode = node.SelectSingleNode("out");
+ XmlNode endNode = node.SelectSingleNode("end");
+ if (endNode != null)
+ {
+ p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(endNode.InnerText));
+ }
+ else if (outNode != null)
+ {
+ p.EndTime.TotalMilliseconds = FramesToMilliseconds(Convert.ToInt32(outNode.InnerText));
+ }
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProTest2Xml.cs b/src/libse/SubtitleFormats/FinalCutProTest2Xml.cs
similarity index 98%
rename from libse/SubtitleFormats/FinalCutProTest2Xml.cs
rename to src/libse/SubtitleFormats/FinalCutProTest2Xml.cs
index 8c2b6b99a..4659da414 100644
--- a/libse/SubtitleFormats/FinalCutProTest2Xml.cs
+++ b/src/libse/SubtitleFormats/FinalCutProTest2Xml.cs
@@ -1,313 +1,313 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- // - Mom, when you were my age
what did you want to do?
- public class FinalCutProTest2Xml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Final Cut Pro Test2 Xml";
-
- public static string GetFrameRateAsString()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "24"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "24";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 29)
- {
- return "25";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 30)
- {
- return "30"; // ntsc 29.97
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 40)
- {
- return "30";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 60)
- {
- return "60"; // ntsc 59.94
- }
-
- return "60";
- }
-
- public static string GetNtsc()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "TRUE"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "FALSE";
- }
-
- return "TRUE";
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- int duration = 0;
- if (subtitle.Paragraphs.Count > 0)
- {
- duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
- }
-
- string seString = "Subtitle Edit at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " EC466A7D-8B45-4682-9978-D15D630C882Eadd" + seString + "" + duration + ">" + GetNtsc() + @"" + GetFrameRateAsString() + @"" + GetNtsc() + @"" + GetFrameRateAsString() + @"01:00:00:0090000NDF-1-1";
-
- const string xmlTrackStructure = "Text3000FALSE251375148615041615FALSEblackFALSETextTextTextgeneratorvideostrTextA finales de los años sesenta, una joven pareja, Guy y Rosemary, fontnameFontLucida GrandefontsizeSize01000[FONTSIZE]fontstyleStyle14Plain1Bold2Italic3Bold/Italic41fontalignAlignment13Left1Center2Right32fontcolorFont Color255255255255originOrigin00fonttrackTracking-2002001leadingLeading-1001000aspectAspect0.151autokernAuto KerningTRUEsubpixelUse SubpixelTRUEBasic MotionbasicmotionmotionvideoscaleScale01000100rotationRotation-864086400centerCenter0.004709580.396648centerOffsetAnchor Point00video3506ED18-CB4D-41B8-A760-4D42356E4F321E6E96FD-94F6-4975-BDFE-7B360E909111";
-
- if (string.IsNullOrEmpty(title))
- {
- title = "Subtitle Edit subtitle";
- }
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
- xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
- if (!string.IsNullOrEmpty(subtitle.Header))
- {
- var header = new XmlDocument();
- try
- {
- header.LoadXml(subtitle.Header);
- var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
- if (node != null)
- {
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
- }
- }
- catch
- {
- }
- }
-
- XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track[2]");
-
- const string newLine = "_____@___";
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode generatorItem = xml.CreateElement("generatoritem");
- string fontStyle = "1"; //1==plain
- var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
- if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
- {
- fontStyle = "4"; //4==bold/italic
- }
- else if (s.StartsWith("") && s.EndsWith(""))
- {
- fontStyle = "3"; //3==italic
- }
-
- generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture));
-
- double frameRate = Configuration.Settings.General.CurrentFrameRate;
- XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
- start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
- end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
- text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
-
- trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
- xmlAsText = xmlAsText.Replace(newLine, "
");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var frameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- var header = new XmlDocument { XmlResolver = null };
- header.LoadXml(sb.ToString());
- if (header.SelectSingleNode("sequence/media/video/track") != null)
- {
- header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
- }
-
- subtitle.Header = header.OuterXml;
-
- if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
- {
- try
- {
- frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
- }
- catch
- {
- frameRate = Configuration.Settings.General.CurrentFrameRate;
- }
- }
-
- foreach (XmlNode node in xml.SelectNodes("//video/track"))
- {
- try
- {
- foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
- {
- XmlNode rate = generatorItemNode.SelectSingleNode("rate");
- XmlNode timebase = rate?.SelectSingleNode("timebase");
- if (timebase != null)
- {
- frameRate = double.Parse(timebase.InnerText);
- }
-
- double startFrame = 0;
- double endFrame = 0;
- XmlNode startNode = generatorItemNode.SelectSingleNode("start");
- if (startNode != null)
- {
- startFrame = double.Parse(startNode.InnerText);
- }
-
- XmlNode endNode = generatorItemNode.SelectSingleNode("end");
- if (endNode != null)
- {
- endFrame = double.Parse(endNode.InnerText);
- }
-
- string text = string.Empty;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
-
- bool italic = false;
- bool bold = false;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- if (!bold && !italic)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- }
-
- if (text.Length > 0)
- {
- if (!text.Contains(Environment.NewLine))
- {
- text = text.Replace("\r", Environment.NewLine);
- }
-
- if (bold)
- {
- text = "" + text + "";
- }
-
- if (italic)
- {
- text = "" + text + "";
- }
-
- subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- return;
- }
- Configuration.Settings.General.CurrentFrameRate = frameRate;
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ // - Mom, when you were my age
what did you want to do?
+ public class FinalCutProTest2Xml : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Final Cut Pro Test2 Xml";
+
+ public static string GetFrameRateAsString()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "24"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "24";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 29)
+ {
+ return "25";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 30)
+ {
+ return "30"; // ntsc 29.97
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 40)
+ {
+ return "30";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 60)
+ {
+ return "60"; // ntsc 59.94
+ }
+
+ return "60";
+ }
+
+ public static string GetNtsc()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "TRUE"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "FALSE";
+ }
+
+ return "TRUE";
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ int duration = 0;
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
+ }
+
+ string seString = "Subtitle Edit at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " EC466A7D-8B45-4682-9978-D15D630C882Eadd" + seString + "" + duration + ">" + GetNtsc() + @"" + GetFrameRateAsString() + @"" + GetNtsc() + @"" + GetFrameRateAsString() + @"01:00:00:0090000NDF-1-1";
+
+ const string xmlTrackStructure = "Text3000FALSE251375148615041615FALSEblackFALSETextTextTextgeneratorvideostrTextA finales de los años sesenta, una joven pareja, Guy y Rosemary, fontnameFontLucida GrandefontsizeSize01000[FONTSIZE]fontstyleStyle14Plain1Bold2Italic3Bold/Italic41fontalignAlignment13Left1Center2Right32fontcolorFont Color255255255255originOrigin00fonttrackTracking-2002001leadingLeading-1001000aspectAspect0.151autokernAuto KerningTRUEsubpixelUse SubpixelTRUEBasic MotionbasicmotionmotionvideoscaleScale01000100rotationRotation-864086400centerCenter0.004709580.396648centerOffsetAnchor Point00video3506ED18-CB4D-41B8-A760-4D42356E4F321E6E96FD-94F6-4975-BDFE-7B360E909111";
+
+ if (string.IsNullOrEmpty(title))
+ {
+ title = "Subtitle Edit subtitle";
+ }
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
+ xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
+ if (!string.IsNullOrEmpty(subtitle.Header))
+ {
+ var header = new XmlDocument();
+ try
+ {
+ header.LoadXml(subtitle.Header);
+ var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
+ if (node != null)
+ {
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track[2]");
+
+ const string newLine = "_____@___";
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode generatorItem = xml.CreateElement("generatoritem");
+ string fontStyle = "1"; //1==plain
+ var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
+ if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
+ {
+ fontStyle = "4"; //4==bold/italic
+ }
+ else if (s.StartsWith("") && s.EndsWith(""))
+ {
+ fontStyle = "3"; //3==italic
+ }
+
+ generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture));
+
+ double frameRate = Configuration.Settings.General.CurrentFrameRate;
+ XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
+ start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
+ end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+ text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
+
+ trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
+ xmlAsText = xmlAsText.Replace(newLine, "
");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var frameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ var header = new XmlDocument { XmlResolver = null };
+ header.LoadXml(sb.ToString());
+ if (header.SelectSingleNode("sequence/media/video/track") != null)
+ {
+ header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
+ }
+
+ subtitle.Header = header.OuterXml;
+
+ if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
+ {
+ try
+ {
+ frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
+ }
+ catch
+ {
+ frameRate = Configuration.Settings.General.CurrentFrameRate;
+ }
+ }
+
+ foreach (XmlNode node in xml.SelectNodes("//video/track"))
+ {
+ try
+ {
+ foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
+ {
+ XmlNode rate = generatorItemNode.SelectSingleNode("rate");
+ XmlNode timebase = rate?.SelectSingleNode("timebase");
+ if (timebase != null)
+ {
+ frameRate = double.Parse(timebase.InnerText);
+ }
+
+ double startFrame = 0;
+ double endFrame = 0;
+ XmlNode startNode = generatorItemNode.SelectSingleNode("start");
+ if (startNode != null)
+ {
+ startFrame = double.Parse(startNode.InnerText);
+ }
+
+ XmlNode endNode = generatorItemNode.SelectSingleNode("end");
+ if (endNode != null)
+ {
+ endFrame = double.Parse(endNode.InnerText);
+ }
+
+ string text = string.Empty;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+
+ bool italic = false;
+ bool bold = false;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ if (!bold && !italic)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (text.Length > 0)
+ {
+ if (!text.Contains(Environment.NewLine))
+ {
+ text = text.Replace("\r", Environment.NewLine);
+ }
+
+ if (bold)
+ {
+ text = "" + text + "";
+ }
+
+ if (italic)
+ {
+ text = "" + text + "";
+ }
+
+ subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+ Configuration.Settings.General.CurrentFrameRate = frameRate;
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProTextXml.cs b/src/libse/SubtitleFormats/FinalCutProTextXml.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProTextXml.cs
rename to src/libse/SubtitleFormats/FinalCutProTextXml.cs
index 1c710a023..bd6fd67e9 100644
--- a/libse/SubtitleFormats/FinalCutProTextXml.cs
+++ b/src/libse/SubtitleFormats/FinalCutProTextXml.cs
@@ -1,398 +1,398 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- // - Mom, when you were my age
what did you want to do?
- public class FinalCutProTestXml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Final Cut Pro Test Xml";
-
- public static string GetFrameRateAsString()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "24"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "24";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 29)
- {
- return "25";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 30)
- {
- return "30"; // ntsc 29.97
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 40)
- {
- return "30";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 60)
- {
- return "60"; // ntsc 59.94
- }
-
- return "60";
- }
-
- public static string GetNtsc()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "TRUE"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "FALSE";
- }
-
- return "TRUE";
- //if (Configuration.Settings.General.CurrentFrameRate < 29)
- // return "FALSE";
- //if (Configuration.Settings.General.CurrentFrameRate < 29)
- // return "FALSE";
- //if (Configuration.Settings.General.CurrentFrameRate < 30)
- // return "TRUE"; // ntsc 29.97
- //if (Configuration.Settings.General.CurrentFrameRate < 40)
- // return "TRUE";
- //if (Configuration.Settings.General.CurrentFrameRate < 40)
- // return "TRUE";
- //if (Configuration.Settings.General.CurrentFrameRate < 60)
- // return "TRUE"; // ntsc 59.94
- //return "FALSE";
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- int duration = 0;
- if (subtitle.Paragraphs.Count > 0)
- {
- duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8
- add
- X
- " + duration + @"
-
- " + GetNtsc() + @"
- " + GetFrameRateAsString() + @"
-
-
-
- " + GetNtsc() + @"
- " + GetFrameRateAsString() + @"
-
- 00:00:00:00
- 0
-
- NDF
-
- 0
- " + duration + @"
-
-
-
-
-";
-
- if (string.IsNullOrEmpty(title))
- {
- title = "Subtitle Edit subtitle";
- }
-
- string xmlTrackStructure = "Text0" + GetNtsc() + @"" + GetFrameRateAsString() + @"046TextTextTextgeneratorvideostrText[TEXT]fontnameFontArialfontsizeSize0100032fontstyleStyle14Plain1Bold2Italic3Bold/Italic1[FONTSTYLE]fontalignAlignment13Left1Center2Right32fontcolorFont Color1255255255originOrigin00.233854";
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
- xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
-
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
- if (!string.IsNullOrEmpty(subtitle.Header))
- {
- var header = new XmlDocument();
- try
- {
- header.LoadXml(subtitle.Header);
- var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
- if (node != null)
- {
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
- }
- }
- catch
- {
- }
- }
-
- XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track");
-
- const string newLine = "_____@___";
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode generatorItem = xml.CreateElement("generatoritem");
- string fontStyle = "1"; //1==plain
- var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
- if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
- {
- fontStyle = "4"; //4==bold/italic
- }
- else if (s.StartsWith("") && s.EndsWith(""))
- {
- fontStyle = "3"; //3==italic
- }
-
- generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle);
-
- double frameRate = Configuration.Settings.General.CurrentFrameRate;
- XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
- start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
- end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
- text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
-
- trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
- xmlAsText = xmlAsText.Replace(newLine, "
");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var frameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- var header = new XmlDocument { XmlResolver = null };
- header.LoadXml(sb.ToString());
- if (header.SelectSingleNode("sequence/media/video/track") != null)
- {
- header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
- }
-
- subtitle.Header = header.OuterXml;
-
- if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
- {
- try
- {
- frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
- }
- catch
- {
- frameRate = Configuration.Settings.General.CurrentFrameRate;
- }
- }
-
- foreach (XmlNode node in xml.SelectNodes("//media/video/track"))
- {
- try
- {
- foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
- {
- XmlNode rate = generatorItemNode.SelectSingleNode("rate");
- if (rate != null)
- {
- XmlNode timebase = rate.SelectSingleNode("timebase");
- if (timebase != null)
- {
- frameRate = double.Parse(timebase.InnerText);
- }
- }
-
- double startFrame = 0;
- double endFrame = 0;
- XmlNode startNode = generatorItemNode.SelectSingleNode("start");
- if (startNode != null)
- {
- startFrame = double.Parse(startNode.InnerText);
- }
-
- XmlNode endNode = generatorItemNode.SelectSingleNode("end");
- if (endNode != null)
- {
- endFrame = double.Parse(endNode.InnerText);
- }
-
- string text = string.Empty;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
-
- bool italic = false;
- bool bold = false;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- if (!bold && !italic)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- }
-
- if (text.Length > 0)
- {
- if (!text.Contains(Environment.NewLine))
- {
- text = text.Replace("\r", Environment.NewLine);
- }
-
- if (bold)
- {
- text = "" + text + "";
- }
-
- if (italic)
- {
- text = "" + text + "";
- }
-
- subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- return;
- }
- Configuration.Settings.General.CurrentFrameRate = frameRate;
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ // - Mom, when you were my age
what did you want to do?
+ public class FinalCutProTestXml : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Final Cut Pro Test Xml";
+
+ public static string GetFrameRateAsString()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "24"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "24";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 29)
+ {
+ return "25";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 30)
+ {
+ return "30"; // ntsc 29.97
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 40)
+ {
+ return "30";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 60)
+ {
+ return "60"; // ntsc 59.94
+ }
+
+ return "60";
+ }
+
+ public static string GetNtsc()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "TRUE"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "FALSE";
+ }
+
+ return "TRUE";
+ //if (Configuration.Settings.General.CurrentFrameRate < 29)
+ // return "FALSE";
+ //if (Configuration.Settings.General.CurrentFrameRate < 29)
+ // return "FALSE";
+ //if (Configuration.Settings.General.CurrentFrameRate < 30)
+ // return "TRUE"; // ntsc 29.97
+ //if (Configuration.Settings.General.CurrentFrameRate < 40)
+ // return "TRUE";
+ //if (Configuration.Settings.General.CurrentFrameRate < 40)
+ // return "TRUE";
+ //if (Configuration.Settings.General.CurrentFrameRate < 60)
+ // return "TRUE"; // ntsc 59.94
+ //return "FALSE";
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ int duration = 0;
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8
+ add
+ X
+ " + duration + @"
+
+ " + GetNtsc() + @"
+ " + GetFrameRateAsString() + @"
+
+
+
+ " + GetNtsc() + @"
+ " + GetFrameRateAsString() + @"
+
+ 00:00:00:00
+ 0
+
+ NDF
+
+ 0
+ " + duration + @"
+
+
+
+
+";
+
+ if (string.IsNullOrEmpty(title))
+ {
+ title = "Subtitle Edit subtitle";
+ }
+
+ string xmlTrackStructure = "Text0" + GetNtsc() + @"" + GetFrameRateAsString() + @"046TextTextTextgeneratorvideostrText[TEXT]fontnameFontArialfontsizeSize0100032fontstyleStyle14Plain1Bold2Italic3Bold/Italic1[FONTSTYLE]fontalignAlignment13Left1Center2Right32fontcolorFont Color1255255255originOrigin00.233854";
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
+ xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
+
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
+ if (!string.IsNullOrEmpty(subtitle.Header))
+ {
+ var header = new XmlDocument();
+ try
+ {
+ header.LoadXml(subtitle.Header);
+ var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
+ if (node != null)
+ {
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track");
+
+ const string newLine = "_____@___";
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode generatorItem = xml.CreateElement("generatoritem");
+ string fontStyle = "1"; //1==plain
+ var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
+ if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
+ {
+ fontStyle = "4"; //4==bold/italic
+ }
+ else if (s.StartsWith("") && s.EndsWith(""))
+ {
+ fontStyle = "3"; //3==italic
+ }
+
+ generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle);
+
+ double frameRate = Configuration.Settings.General.CurrentFrameRate;
+ XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
+ start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
+ end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+ text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
+
+ trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
+ xmlAsText = xmlAsText.Replace(newLine, "
");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var frameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ var header = new XmlDocument { XmlResolver = null };
+ header.LoadXml(sb.ToString());
+ if (header.SelectSingleNode("sequence/media/video/track") != null)
+ {
+ header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
+ }
+
+ subtitle.Header = header.OuterXml;
+
+ if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
+ {
+ try
+ {
+ frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
+ }
+ catch
+ {
+ frameRate = Configuration.Settings.General.CurrentFrameRate;
+ }
+ }
+
+ foreach (XmlNode node in xml.SelectNodes("//media/video/track"))
+ {
+ try
+ {
+ foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
+ {
+ XmlNode rate = generatorItemNode.SelectSingleNode("rate");
+ if (rate != null)
+ {
+ XmlNode timebase = rate.SelectSingleNode("timebase");
+ if (timebase != null)
+ {
+ frameRate = double.Parse(timebase.InnerText);
+ }
+ }
+
+ double startFrame = 0;
+ double endFrame = 0;
+ XmlNode startNode = generatorItemNode.SelectSingleNode("start");
+ if (startNode != null)
+ {
+ startFrame = double.Parse(startNode.InnerText);
+ }
+
+ XmlNode endNode = generatorItemNode.SelectSingleNode("end");
+ if (endNode != null)
+ {
+ endFrame = double.Parse(endNode.InnerText);
+ }
+
+ string text = string.Empty;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+
+ bool italic = false;
+ bool bold = false;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ if (!bold && !italic)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (text.Length > 0)
+ {
+ if (!text.Contains(Environment.NewLine))
+ {
+ text = text.Replace("\r", Environment.NewLine);
+ }
+
+ if (bold)
+ {
+ text = "" + text + "";
+ }
+
+ if (italic)
+ {
+ text = "" + text + "";
+ }
+
+ subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+ Configuration.Settings.General.CurrentFrameRate = frameRate;
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXCM.cs b/src/libse/SubtitleFormats/FinalCutProXCM.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProXCM.cs
rename to src/libse/SubtitleFormats/FinalCutProXCM.cs
index e10bb7e68..d19551e02 100644
--- a/libse/SubtitleFormats/FinalCutProXCM.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXCM.cs
@@ -1,142 +1,142 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXCM : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Pro X Chapter Marker";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/clip");
-
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode chapterMarker = xml.CreateElement("chapter-marker");
-
- var attr = xml.CreateAttribute("duration");
- attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- chapterMarker.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("start");
- attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
- chapterMarker.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("value");
- attr.Value = p.Text.Replace(Environment.NewLine, Convert.ToChar(8232).ToString());
- chapterMarker.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("posterOffset");
- attr.Value = "11/24s";
- chapterMarker.Attributes.Append(attr);
-
- videoNode.AppendChild(chapterMarker);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- xml.PreserveWhitespace = true;
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip/chapter-marker"))
- {
- try
- {
- var p = new Paragraph();
- p.Text = node.Attributes["value"].InnerText;
- p.Text = p.Text.Replace(Convert.ToChar(8232).ToString(), Environment.NewLine);
- p.StartTime = DecodeTime(node.Attributes["start"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.Attributes["duration"]).TotalMilliseconds;
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
- else if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXCM : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Pro X Chapter Marker";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/clip");
+
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode chapterMarker = xml.CreateElement("chapter-marker");
+
+ var attr = xml.CreateAttribute("duration");
+ attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ chapterMarker.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("start");
+ attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
+ chapterMarker.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("value");
+ attr.Value = p.Text.Replace(Environment.NewLine, Convert.ToChar(8232).ToString());
+ chapterMarker.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("posterOffset");
+ attr.Value = "11/24s";
+ chapterMarker.Attributes.Append(attr);
+
+ videoNode.AppendChild(chapterMarker);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.PreserveWhitespace = true;
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip/chapter-marker"))
+ {
+ try
+ {
+ var p = new Paragraph();
+ p.Text = node.Attributes["value"].InnerText;
+ p.Text = p.Text.Replace(Convert.ToChar(8232).ToString(), Environment.NewLine);
+ p.StartTime = DecodeTime(node.Attributes["start"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.Attributes["duration"]).TotalMilliseconds;
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+ else if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXXml.cs b/src/libse/SubtitleFormats/FinalCutProXXml.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProXXml.cs
rename to src/libse/SubtitleFormats/FinalCutProXXml.cs
index be339edc7..c2a53b3f2 100644
--- a/libse/SubtitleFormats/FinalCutProXXml.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXXml.cs
@@ -1,176 +1,176 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXXml : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Pro X Xml";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- //
- //
- //
- //
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlClipStructure =
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " ";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine");
-
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode clip = xml.CreateElement("clip");
- clip.InnerXml = xmlClipStructure;
- var attr = xml.CreateAttribute("name");
- attr.Value = title;
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("duration");
- //attr.Value = "9529520/2400000s";
- attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("start");
- //attr.Value = "1201200/2400000s";
- attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("audioStart");
- attr.Value = "0s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("audioDuration");
- attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("tcFormat");
- attr.Value = "NDF";
- clip.Attributes.Append(attr);
-
- XmlNode titleNode = clip.SelectSingleNode("title");
- titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
- titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
-
- XmlNode text = clip.SelectSingleNode("title/text");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
-
- videoNode.AppendChild(clip);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip"))
- {
- try
- {
- foreach (XmlNode title in node.SelectNodes("title"))
- {
- var role = title.Attributes["role"];
- if (role != null && role.InnerText == "Subtitles")
- {
- var textNode = title.SelectSingleNode("text");
- if (textNode != null && !string.IsNullOrEmpty(textNode.InnerText))
- {
- string text = textNode.InnerText;
- Paragraph p = new Paragraph();
- p.Text = text.Trim();
- p.StartTime = DecodeTime(title.Attributes["offset"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds;
- subtitle.Paragraphs.Add(p);
- }
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
- else if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXXml : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Pro X Xml";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ //
+ //
+ //
+ //
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlClipStructure =
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " ";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine");
+
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode clip = xml.CreateElement("clip");
+ clip.InnerXml = xmlClipStructure;
+ var attr = xml.CreateAttribute("name");
+ attr.Value = title;
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("duration");
+ //attr.Value = "9529520/2400000s";
+ attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("start");
+ //attr.Value = "1201200/2400000s";
+ attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("audioStart");
+ attr.Value = "0s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("audioDuration");
+ attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("tcFormat");
+ attr.Value = "NDF";
+ clip.Attributes.Append(attr);
+
+ XmlNode titleNode = clip.SelectSingleNode("title");
+ titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
+ titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+
+ XmlNode text = clip.SelectSingleNode("title/text");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+
+ videoNode.AppendChild(clip);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/clip"))
+ {
+ try
+ {
+ foreach (XmlNode title in node.SelectNodes("title"))
+ {
+ var role = title.Attributes["role"];
+ if (role != null && role.InnerText == "Subtitles")
+ {
+ var textNode = title.SelectSingleNode("text");
+ if (textNode != null && !string.IsNullOrEmpty(textNode.InnerText))
+ {
+ string text = textNode.InnerText;
+ Paragraph p = new Paragraph();
+ p.Text = text.Trim();
+ p.StartTime = DecodeTime(title.Attributes["offset"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds;
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+ else if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXml.cs b/src/libse/SubtitleFormats/FinalCutProXml.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProXml.cs
rename to src/libse/SubtitleFormats/FinalCutProXml.cs
index 0b2332574..d8dcd9b1f 100644
--- a/libse/SubtitleFormats/FinalCutProXml.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXml.cs
@@ -1,657 +1,657 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- // - Mom, when you were my age
what did you want to do?
- public class FinalCutProXml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Final Cut Pro Xml";
-
- public static string GetFrameRateAsString()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "24"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "24";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 29)
- {
- return "25";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 30)
- {
- return "30"; // ntsc 29.97
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 40)
- {
- return "30";
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 60)
- {
- return "60"; // ntsc 59.94
- }
-
- return "60";
- }
-
- public static string GetNtsc()
- {
- if (Configuration.Settings.General.CurrentFrameRate < 24)
- {
- return "TRUE"; // ntsc 23.976
- }
-
- if (Configuration.Settings.General.CurrentFrameRate < 25)
- {
- return "FALSE";
- }
-
- return "TRUE";
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- int duration = 0;
- if (subtitle.Paragraphs.Count > 0)
- {
- duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8
- add
- X
- " + duration + @"
-
- " + GetNtsc() + @"
- " + GetFrameRateAsString() + @"
-
-
-
- " + GetNtsc() + @"
- " + GetFrameRateAsString() + @"
-
- 00:00:00:00
- 0
-
- NDF
-
- 0
- " + duration + @"
-
-
-
-
-";
-
- string xmlTrackStructure =
- @"
- Outline Text
- 3000
-
- " + GetNtsc() + @"
- " + GetFrameRateAsString() + @"
-
- 1380
- 1474
- 8228
- 8322
- TRUE
- FALSE
- black
- Outline Text1
-
- Outline Text
- Outline Text
- Text
- generator
- video
-
- part1
- Text Settings
-
-
-
- str
- Text
- [TEXT]
-
-
- font
- Font
- [FONTNAME]
-
-
- style
- Style
- 1
- 4
-
-
- Plain
- 1
-
-
- Bold
- 2
-
-
- Italic
- 3
-
-
- Bold/Italic
- 4
-
-
- [FONTSTYLE]
-
-
- align
- Alignment
- 1
- 3
-
-
- Left
- 1
-
-
- Center
- 2
-
-
- Right
- 3
-
-
- 2
-
-
- size
- Size
- 0
- 200
- [FONTSIZE]
-
-
- track
- Tracking
- 0
- 100
- 1
-
-
- lead
- Leading
- -100
- 100
- 0
-
-
- aspect
- Aspect
- 0
- 4
- 1
-
-
- linewidth
- Line Width
- 0
- 200
- 20
-
-
- linesoft
- Line Softness
- 0
- 100
- 5
-
-
- textopacity
- Text Opacity
- 0
- 100
- 100
-
-
- center
- Center
-
- 0.00833333
- 0.390741
-
-
-
- textcolor
- Text Color
-
- 255
- 255
- 255
- 255
-
-
-
- supertext
- Text Graphic
-
-
- linecolor
- Line Color
-
- 255
- 0
- 0
- 0
-
-
-
- part2
- Background Settings
-
-
-
- xscale
- Horizontal Size
- 0
- 200
- 0
-
-
- yscale
- Vertical Size
- 0
- 200
- 0
-
-
- xoffset
- Horizontal Offset
- -100
- 100
- 0
-
-
- yoffset
- Vertical Offset
- -100
- 100
- 0
-
-
- backcolor
- Back Color
-
- 255
- 255
- 255
- 255
-
-
-
- superback
- Back Graphic
-
-
- crop
- Crop
- FALSE
-
-
- autokern
- Auto Kerning
- TRUE
-
-
-
- video
-
- ";
-
- if (string.IsNullOrEmpty(title))
- {
- title = "Subtitle Edit subtitle";
- }
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
- xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
- if (!string.IsNullOrEmpty(subtitle.Header))
- {
- var header = new XmlDocument();
- try
- {
- header.LoadXml(subtitle.Header);
- var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
- if (node != null)
- {
- xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
- }
- }
- catch
- {
- // ignored
- }
- }
-
- XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track");
-
- const string newLine = "_____@___";
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode generatorItem = xml.CreateElement("generatoritem");
- string fontStyle = "1"; //1==plain
- var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
- if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
- {
- fontStyle = "4"; //4==bold/italic
- }
- else if (s.StartsWith("") && s.EndsWith(""))
- {
- fontStyle = "3"; //3==italic
- }
-
- generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).
- Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)).
- Replace("[FONTNAME]", Configuration.Settings.SubtitleSettings.FcpFontName).
- Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture));
-
- double frameRate = Configuration.Settings.General.CurrentFrameRate;
- XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
- start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
- end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
-
- XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
- text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
- text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
-
- trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
- xmlAsText = xmlAsText.Replace(newLine, "
");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- var frameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
- var header = new XmlDocument { XmlResolver = null };
- header.LoadXml(sb.ToString());
- if (header.SelectSingleNode("sequence/media/video/track") != null)
- {
- header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
- }
-
- subtitle.Header = header.OuterXml;
-
- if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
- {
- try
- {
- frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
- }
- catch
- {
- frameRate = Configuration.Settings.General.CurrentFrameRate;
- }
- }
-
- foreach (XmlNode node in xml.SelectNodes("//video/track"))
- {
- try
- {
- foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
- {
- XmlNode rate = generatorItemNode.SelectSingleNode("rate");
- if (rate != null)
- {
- XmlNode timebase = rate.SelectSingleNode("timebase");
- if (timebase != null)
- {
- frameRate = double.Parse(timebase.InnerText);
- }
- }
-
- double startFrame = 0;
- double endFrame = 0;
- XmlNode startNode = generatorItemNode.SelectSingleNode("start");
- if (startNode != null)
- {
- startFrame = double.Parse(startNode.InnerText);
- }
-
- XmlNode endNode = generatorItemNode.SelectSingleNode("end");
- if (endNode != null)
- {
- endFrame = double.Parse(endNode.InnerText);
- }
-
- string text = string.Empty;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
- if (text.Length == 0)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str1']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
- }
- if (text.Length == 0)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str2']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
- }
- if (text.Length == 0)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='sourcetext']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
- }
- if (text.Length == 0)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='text']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- if (valueNode != null)
- {
- text += valueNode.InnerText;
- }
- }
- }
-
- bool italic = false;
- bool bold = false;
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- if (!bold && !italic)
- {
- foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
- {
- XmlNode valueNode = parameterNode.SelectSingleNode("value");
- var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
- if (valueNode != null)
- {
- int no;
- if (int.TryParse(valueNode.InnerText, out no))
- {
- no--;
- if (no < valueEntries.Count)
- {
- var styleNameNode = valueEntries[no].SelectSingleNode("name");
- if (styleNameNode != null)
- {
- string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
- italic = styleName == "italic" || styleName == "bold/italic";
- bold = styleName == "bold" || styleName == "bold/italic";
- }
- }
- }
- }
- }
- }
-
- if (text.Length > 0)
- {
- if (!text.Contains(Environment.NewLine))
- {
- text = text.Replace("\r", Environment.NewLine);
- }
-
- if (bold)
- {
- text = "" + text + "";
- }
-
- if (italic)
- {
- text = "" + text + "";
- }
-
- subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- return;
- }
- Configuration.Settings.General.CurrentFrameRate = frameRate;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ // - Mom, when you were my age
what did you want to do?
+ public class FinalCutProXml : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Final Cut Pro Xml";
+
+ public static string GetFrameRateAsString()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "24"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "24";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 29)
+ {
+ return "25";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 30)
+ {
+ return "30"; // ntsc 29.97
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 40)
+ {
+ return "30";
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 60)
+ {
+ return "60"; // ntsc 59.94
+ }
+
+ return "60";
+ }
+
+ public static string GetNtsc()
+ {
+ if (Configuration.Settings.General.CurrentFrameRate < 24)
+ {
+ return "TRUE"; // ntsc 23.976
+ }
+
+ if (Configuration.Settings.General.CurrentFrameRate < 25)
+ {
+ return "FALSE";
+ }
+
+ return "TRUE";
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ int duration = 0;
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ duration = (int)Math.Round(subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].EndTime.TotalSeconds * Configuration.Settings.General.CurrentFrameRate);
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ @" 5B3B0C07-9A9D-42AA-872C-C953923F97D8
+ add
+ X
+ " + duration + @"
+
+ " + GetNtsc() + @"
+ " + GetFrameRateAsString() + @"
+
+
+
+ " + GetNtsc() + @"
+ " + GetFrameRateAsString() + @"
+
+ 00:00:00:00
+ 0
+
+ NDF
+
+ 0
+ " + duration + @"
+
+
+
+
+";
+
+ string xmlTrackStructure =
+ @"
+ Outline Text
+ 3000
+
+ " + GetNtsc() + @"
+ " + GetFrameRateAsString() + @"
+
+ 1380
+ 1474
+ 8228
+ 8322
+ TRUE
+ FALSE
+ black
+ Outline Text1
+
+ Outline Text
+ Outline Text
+ Text
+ generator
+ video
+
+ part1
+ Text Settings
+
+
+
+ str
+ Text
+ [TEXT]
+
+
+ font
+ Font
+ [FONTNAME]
+
+
+ style
+ Style
+ 1
+ 4
+
+
+ Plain
+ 1
+
+
+ Bold
+ 2
+
+
+ Italic
+ 3
+
+
+ Bold/Italic
+ 4
+
+
+ [FONTSTYLE]
+
+
+ align
+ Alignment
+ 1
+ 3
+
+
+ Left
+ 1
+
+
+ Center
+ 2
+
+
+ Right
+ 3
+
+
+ 2
+
+
+ size
+ Size
+ 0
+ 200
+ [FONTSIZE]
+
+
+ track
+ Tracking
+ 0
+ 100
+ 1
+
+
+ lead
+ Leading
+ -100
+ 100
+ 0
+
+
+ aspect
+ Aspect
+ 0
+ 4
+ 1
+
+
+ linewidth
+ Line Width
+ 0
+ 200
+ 20
+
+
+ linesoft
+ Line Softness
+ 0
+ 100
+ 5
+
+
+ textopacity
+ Text Opacity
+ 0
+ 100
+ 100
+
+
+ center
+ Center
+
+ 0.00833333
+ 0.390741
+
+
+
+ textcolor
+ Text Color
+
+ 255
+ 255
+ 255
+ 255
+
+
+
+ supertext
+ Text Graphic
+
+
+ linecolor
+ Line Color
+
+ 255
+ 0
+ 0
+ 0
+
+
+
+ part2
+ Background Settings
+
+
+
+ xscale
+ Horizontal Size
+ 0
+ 200
+ 0
+
+
+ yscale
+ Vertical Size
+ 0
+ 200
+ 0
+
+
+ xoffset
+ Horizontal Offset
+ -100
+ 100
+ 0
+
+
+ yoffset
+ Vertical Offset
+ -100
+ 100
+ 0
+
+
+ backcolor
+ Back Color
+
+ 255
+ 255
+ 255
+ 255
+
+
+
+ superback
+ Back Graphic
+
+
+ crop
+ Crop
+ FALSE
+
+
+ autokern
+ Auto Kerning
+ TRUE
+
+
+
+ video
+
+ ";
+
+ if (string.IsNullOrEmpty(title))
+ {
+ title = "Subtitle Edit subtitle";
+ }
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ xml.DocumentElement.SelectSingleNode("sequence").Attributes["id"].Value = title;
+ xml.DocumentElement.SelectSingleNode("sequence/name").InnerText = title;
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = Guid.NewGuid().ToString().ToUpperInvariant();
+ if (!string.IsNullOrEmpty(subtitle.Header))
+ {
+ var header = new XmlDocument();
+ try
+ {
+ header.LoadXml(subtitle.Header);
+ var node = header.DocumentElement.SelectSingleNode("sequence/uuid");
+ if (node != null)
+ {
+ xml.DocumentElement.SelectSingleNode("sequence/uuid").InnerText = node.InnerText;
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ XmlNode trackNode = xml.DocumentElement.SelectSingleNode("sequence/media/video/track");
+
+ const string newLine = "_____@___";
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode generatorItem = xml.CreateElement("generatoritem");
+ string fontStyle = "1"; //1==plain
+ var s = HtmlUtil.RemoveOpenCloseTags(p.Text, HtmlUtil.TagFont).Trim();
+ if ((s.StartsWith("") && s.EndsWith("")) || (s.StartsWith("") && s.EndsWith("")))
+ {
+ fontStyle = "4"; //4==bold/italic
+ }
+ else if (s.StartsWith("") && s.EndsWith(""))
+ {
+ fontStyle = "3"; //3==italic
+ }
+
+ generatorItem.InnerXml = xmlTrackStructure.Replace("[NUMBER]", number.ToString()).Replace("[FONTSTYLE]", fontStyle).
+ Replace("[FONTSIZE]", Configuration.Settings.SubtitleSettings.FcpFontSize.ToString(CultureInfo.InvariantCulture)).
+ Replace("[FONTNAME]", Configuration.Settings.SubtitleSettings.FcpFontName).
+ Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture));
+
+ double frameRate = Configuration.Settings.General.CurrentFrameRate;
+ XmlNode start = generatorItem.SelectSingleNode("generatoritem/start");
+ start.InnerText = ((int)Math.Round(p.StartTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode end = generatorItem.SelectSingleNode("generatoritem/end");
+ end.InnerText = ((int)Math.Round(p.EndTime.TotalSeconds * frameRate)).ToString();
+
+ XmlNode text = generatorItem.SelectSingleNode("generatoritem/effect/parameter[parameterid='str']/value");
+ text.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+ text.InnerXml = text.InnerXml.Replace(Environment.NewLine, newLine);
+
+ trackNode.AppendChild(generatorItem.SelectSingleNode("generatoritem"));
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("xmeml[]", "xmeml");
+ xmlAsText = xmlAsText.Replace(newLine, "
");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ var frameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+ var header = new XmlDocument { XmlResolver = null };
+ header.LoadXml(sb.ToString());
+ if (header.SelectSingleNode("sequence/media/video/track") != null)
+ {
+ header.RemoveChild(header.SelectSingleNode("sequence/media/video/track"));
+ }
+
+ subtitle.Header = header.OuterXml;
+
+ if (xml.DocumentElement.SelectSingleNode("sequence/rate") != null && xml.DocumentElement.SelectSingleNode("sequence/rate/timebase") != null)
+ {
+ try
+ {
+ frameRate = double.Parse(xml.DocumentElement.SelectSingleNode("sequence/rate/timebase").InnerText);
+ }
+ catch
+ {
+ frameRate = Configuration.Settings.General.CurrentFrameRate;
+ }
+ }
+
+ foreach (XmlNode node in xml.SelectNodes("//video/track"))
+ {
+ try
+ {
+ foreach (XmlNode generatorItemNode in node.SelectNodes("generatoritem"))
+ {
+ XmlNode rate = generatorItemNode.SelectSingleNode("rate");
+ if (rate != null)
+ {
+ XmlNode timebase = rate.SelectSingleNode("timebase");
+ if (timebase != null)
+ {
+ frameRate = double.Parse(timebase.InnerText);
+ }
+ }
+
+ double startFrame = 0;
+ double endFrame = 0;
+ XmlNode startNode = generatorItemNode.SelectSingleNode("start");
+ if (startNode != null)
+ {
+ startFrame = double.Parse(startNode.InnerText);
+ }
+
+ XmlNode endNode = generatorItemNode.SelectSingleNode("end");
+ if (endNode != null)
+ {
+ endFrame = double.Parse(endNode.InnerText);
+ }
+
+ string text = string.Empty;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+ if (text.Length == 0)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str1']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+ }
+ if (text.Length == 0)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='str2']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+ }
+ if (text.Length == 0)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='sourcetext']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+ }
+ if (text.Length == 0)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='text']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ if (valueNode != null)
+ {
+ text += valueNode.InnerText;
+ }
+ }
+ }
+
+ bool italic = false;
+ bool bold = false;
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='style']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ if (!bold && !italic)
+ {
+ foreach (XmlNode parameterNode in generatorItemNode.SelectNodes("effect/parameter[parameterid='fontstyle']"))
+ {
+ XmlNode valueNode = parameterNode.SelectSingleNode("value");
+ var valueEntries = parameterNode.SelectNodes("valuelist/valueentry");
+ if (valueNode != null)
+ {
+ int no;
+ if (int.TryParse(valueNode.InnerText, out no))
+ {
+ no--;
+ if (no < valueEntries.Count)
+ {
+ var styleNameNode = valueEntries[no].SelectSingleNode("name");
+ if (styleNameNode != null)
+ {
+ string styleName = styleNameNode.InnerText.ToLowerInvariant().Trim();
+ italic = styleName == "italic" || styleName == "bold/italic";
+ bold = styleName == "bold" || styleName == "bold/italic";
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (text.Length > 0)
+ {
+ if (!text.Contains(Environment.NewLine))
+ {
+ text = text.Replace("\r", Environment.NewLine);
+ }
+
+ if (bold)
+ {
+ text = "" + text + "";
+ }
+
+ if (italic)
+ {
+ text = "" + text + "";
+ }
+
+ subtitle.Paragraphs.Add(new Paragraph(text, Convert.ToDouble((startFrame / frameRate) * 1000), Convert.ToDouble((endFrame / frameRate) * 1000)));
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+ Configuration.Settings.General.CurrentFrameRate = frameRate;
+ }
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXml13.cs b/src/libse/SubtitleFormats/FinalCutProXml13.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProXml13.cs
rename to src/libse/SubtitleFormats/FinalCutProXml13.cs
index f148c15a1..888af8a17 100644
--- a/libse/SubtitleFormats/FinalCutProXml13.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXml13.cs
@@ -1,200 +1,200 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXml13 : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Pro Xml 1.3";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlClipStructure =
- " ";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine");
-
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode clip = xml.CreateElement("clip");
- clip.InnerXml = xmlClipStructure;
- var attr = xml.CreateAttribute("name");
- attr.Value = title;
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("duration");
- attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("start");
- attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("audioStart");
- attr.Value = "0s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("audioDuration");
- attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- clip.Attributes.Append(attr);
-
- attr = xml.CreateAttribute("tcFormat");
- attr.Value = "NDF";
- clip.Attributes.Append(attr);
-
- XmlNode titleNode = clip.SelectSingleNode("video");
- titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
- titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
-
- XmlNode param = clip.SelectSingleNode("video/param");
- param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
-
- videoNode.AppendChild(clip);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string x = sb.ToString();
- if (!x.Contains(" 0)
- {
- var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
- if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds)
- {
- add = false;
- }
- }
- if (add)
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- subtitle.Renumber();
- }
- catch (Exception exception)
- {
- System.Diagnostics.Debug.WriteLine(exception.Message);
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
- else if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXml13 : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Pro Xml 1.3";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlClipStructure =
+ " ";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine");
+
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode clip = xml.CreateElement("clip");
+ clip.InnerXml = xmlClipStructure;
+ var attr = xml.CreateAttribute("name");
+ attr.Value = title;
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("duration");
+ attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("start");
+ attr.Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("audioStart");
+ attr.Value = "0s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("audioDuration");
+ attr.Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ clip.Attributes.Append(attr);
+
+ attr = xml.CreateAttribute("tcFormat");
+ attr.Value = "NDF";
+ clip.Attributes.Append(attr);
+
+ XmlNode titleNode = clip.SelectSingleNode("video");
+ titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
+ titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+
+ XmlNode param = clip.SelectSingleNode("video/param");
+ param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+
+ videoNode.AppendChild(clip);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string x = sb.ToString();
+ if (!x.Contains(" 0)
+ {
+ var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
+ if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds)
+ {
+ add = false;
+ }
+ }
+ if (add)
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch (Exception exception)
+ {
+ System.Diagnostics.Debug.WriteLine(exception.Message);
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+ else if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXml14.cs b/src/libse/SubtitleFormats/FinalCutProXml14.cs
similarity index 98%
rename from libse/SubtitleFormats/FinalCutProXml14.cs
rename to src/libse/SubtitleFormats/FinalCutProXml14.cs
index c2b507cc7..4d233d805 100644
--- a/libse/SubtitleFormats/FinalCutProXml14.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXml14.cs
@@ -1,188 +1,188 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXml14 : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Pro Xml 1.4";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " Apple ProRes 422 Proxy" + Environment.NewLine +
- " Linear PCM" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine + // From here down I am unsure how it should be
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlClipStructure =
- " ";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/clip");
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode video = xml.CreateElement("video");
- video.InnerXml = xmlClipStructure;
-
- XmlNode generatorNode = video.SelectSingleNode("video");
- generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
- generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
- generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
-
- XmlNode param = video.SelectSingleNode("video/param");
- param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
-
- videoNode.AppendChild(generatorNode);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string x = sb.ToString();
- if (!x.Contains("") && !x.Contains(""))
- {
- return;
- }
-
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(x.Trim());
-
- foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/param[@name='Text']"))
- {
- try
- {
- string text = node.Attributes["value"].InnerText;
- Paragraph p = new Paragraph();
- p.Text = text.Trim();
- p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- }
- }
-
- if (subtitle.Paragraphs.Count == 0)
- {
- foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/title/text"))
- {
- try
- {
- string text = node.ParentNode.InnerText;
- Paragraph p = new Paragraph();
- p.Text = text.Trim();
- p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
- bool add = true;
- if (subtitle.Paragraphs.Count > 0)
- {
- var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
- if (prev.Text == p.Text && Math.Abs(prev.StartTime.TotalMilliseconds - p.StartTime.TotalMilliseconds) < 0.01)
- {
- add = false;
- }
- }
- if (add)
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
- else if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXml14 : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Pro Xml 1.4";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " Apple ProRes 422 Proxy" + Environment.NewLine +
+ " Linear PCM" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine + // From here down I am unsure how it should be
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlClipStructure =
+ " ";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/clip");
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode video = xml.CreateElement("video");
+ video.InnerXml = xmlClipStructure;
+
+ XmlNode generatorNode = video.SelectSingleNode("video");
+ generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
+ generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 2400000) + "/2400000s";
+ generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 2400000) + "/2400000s";
+
+ XmlNode param = video.SelectSingleNode("video/param");
+ param.Attributes["value"].InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+
+ videoNode.AppendChild(generatorNode);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string x = sb.ToString();
+ if (!x.Contains("") && !x.Contains(""))
+ {
+ return;
+ }
+
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(x.Trim());
+
+ foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/param[@name='Text']"))
+ {
+ try
+ {
+ string text = node.Attributes["value"].InnerText;
+ Paragraph p = new Paragraph();
+ p.Text = text.Trim();
+ p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+
+ if (subtitle.Paragraphs.Count == 0)
+ {
+ foreach (XmlNode node in xml.SelectNodes("//project/sequence/spine/clip/video/title/text"))
+ {
+ try
+ {
+ string text = node.ParentNode.InnerText;
+ Paragraph p = new Paragraph();
+ p.Text = text.Trim();
+ p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
+ bool add = true;
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
+ if (prev.Text == p.Text && Math.Abs(prev.StartTime.TotalMilliseconds - p.StartTime.TotalMilliseconds) < 0.01)
+ {
+ add = false;
+ }
+ }
+ if (add)
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+ else if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXml14Text.cs b/src/libse/SubtitleFormats/FinalCutProXml14Text.cs
similarity index 97%
rename from libse/SubtitleFormats/FinalCutProXml14Text.cs
rename to src/libse/SubtitleFormats/FinalCutProXml14Text.cs
index 4bd5a3fb3..73f89b5dc 100644
--- a/libse/SubtitleFormats/FinalCutProXml14Text.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXml14Text.cs
@@ -1,209 +1,209 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXml14Text : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Pro Xml 1.4 Text";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlClipStructure =
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " THE NOISEMAKER" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/gap");
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode video = xml.CreateElement("video");
- var trimmedTitle = new StringBuilder();
- foreach (var ch in HtmlUtil.RemoveHtmlTags(p.Text, true))
- {
- if (CharUtils.IsEnglishAlphabet(ch) || char.IsDigit(ch))
- {
- trimmedTitle.Append(ch);
- }
- }
- string temp = xmlClipStructure.Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture)).Replace("[TITLEID]", trimmedTitle.ToString());
- video.InnerXml = temp;
-
- XmlNode generatorNode = video.SelectSingleNode("title");
- if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
- {
- generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
- }
- else
- {
- generatorNode.Attributes["offset"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
- }
-
- if (IsNearleWholeNumber(p.Duration.TotalSeconds))
- {
- generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds) + "s";
- }
- else
- {
- generatorNode.Attributes["duration"].Value = FinalCutProXml15.GetFrameTime(p.Duration);
- }
-
- if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
- {
- generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
- }
- else
- {
- generatorNode.Attributes["start"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
- }
-
- XmlNode param = video.SelectSingleNode("title/text/text-style");
- param.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
-
- videoNode.AppendChild(generatorNode);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- private static bool IsNearleWholeNumber(double number)
- {
- double rest = number - Convert.ToInt64(number);
- return rest < 0.001;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string x = sb.ToString();
- if (!x.Contains(""))
- {
- return;
- }
-
- var xml = new XmlDocument();
- try
- {
- xml.LoadXml(x.Trim());
-
- if (subtitle.Paragraphs.Count == 0)
- {
- var textNodes = xml.SelectNodes("//project/sequence/spine/title/text");
- if (textNodes.Count == 0)
- {
- textNodes = xml.SelectNodes("//project/sequence/spine/gap/title/text");
- }
- foreach (XmlNode node in textNodes)
- {
- try
- {
- string text = node.ParentNode.InnerText;
- var p = new Paragraph();
- p.Text = text.Trim();
- p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
- bool add = true;
- if (subtitle.Paragraphs.Count > 0)
- {
- var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
- if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds)
- {
- add = false;
- }
- }
- if (add)
- {
- subtitle.Paragraphs.Add(p);
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
- if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXml14Text : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Pro Xml 1.4 Text";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlClipStructure =
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " THE NOISEMAKER" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("//project/sequence/spine/gap");
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode video = xml.CreateElement("video");
+ var trimmedTitle = new StringBuilder();
+ foreach (var ch in HtmlUtil.RemoveHtmlTags(p.Text, true))
+ {
+ if (CharUtils.IsEnglishAlphabet(ch) || char.IsDigit(ch))
+ {
+ trimmedTitle.Append(ch);
+ }
+ }
+ string temp = xmlClipStructure.Replace("[NUMBER]", number.ToString(CultureInfo.InvariantCulture)).Replace("[TITLEID]", trimmedTitle.ToString());
+ video.InnerXml = temp;
+
+ XmlNode generatorNode = video.SelectSingleNode("title");
+ if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
+ {
+ generatorNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
+ }
+ else
+ {
+ generatorNode.Attributes["offset"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
+ }
+
+ if (IsNearleWholeNumber(p.Duration.TotalSeconds))
+ {
+ generatorNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds) + "s";
+ }
+ else
+ {
+ generatorNode.Attributes["duration"].Value = FinalCutProXml15.GetFrameTime(p.Duration);
+ }
+
+ if (IsNearleWholeNumber(p.StartTime.TotalSeconds))
+ {
+ generatorNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds) + "s";
+ }
+ else
+ {
+ generatorNode.Attributes["start"].Value = FinalCutProXml15.GetFrameTime(p.StartTime);
+ }
+
+ XmlNode param = video.SelectSingleNode("title/text/text-style");
+ param.InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+
+ videoNode.AppendChild(generatorNode);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ private static bool IsNearleWholeNumber(double number)
+ {
+ double rest = number - Convert.ToInt64(number);
+ return rest < 0.001;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string x = sb.ToString();
+ if (!x.Contains(""))
+ {
+ return;
+ }
+
+ var xml = new XmlDocument();
+ try
+ {
+ xml.LoadXml(x.Trim());
+
+ if (subtitle.Paragraphs.Count == 0)
+ {
+ var textNodes = xml.SelectNodes("//project/sequence/spine/title/text");
+ if (textNodes.Count == 0)
+ {
+ textNodes = xml.SelectNodes("//project/sequence/spine/gap/title/text");
+ }
+ foreach (XmlNode node in textNodes)
+ {
+ try
+ {
+ string text = node.ParentNode.InnerText;
+ var p = new Paragraph();
+ p.Text = text.Trim();
+ p.StartTime = DecodeTime(node.ParentNode.Attributes["offset"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(node.ParentNode.Attributes["duration"]).TotalMilliseconds;
+ bool add = true;
+ if (subtitle.Paragraphs.Count > 0)
+ {
+ var prev = subtitle.Paragraphs[subtitle.Paragraphs.Count - 1];
+ if (prev.Text == p.Text && prev.StartTime.TotalMilliseconds == p.StartTime.TotalMilliseconds)
+ {
+ add = false;
+ }
+ }
+ if (add)
+ {
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+ if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalCutProXml15.cs b/src/libse/SubtitleFormats/FinalCutProXml15.cs
similarity index 100%
rename from libse/SubtitleFormats/FinalCutProXml15.cs
rename to src/libse/SubtitleFormats/FinalCutProXml15.cs
diff --git a/libse/SubtitleFormats/FinalCutProXml16.cs b/src/libse/SubtitleFormats/FinalCutProXml16.cs
similarity index 100%
rename from libse/SubtitleFormats/FinalCutProXml16.cs
rename to src/libse/SubtitleFormats/FinalCutProXml16.cs
diff --git a/libse/SubtitleFormats/FinalCutProXml17.cs b/src/libse/SubtitleFormats/FinalCutProXml17.cs
similarity index 100%
rename from libse/SubtitleFormats/FinalCutProXml17.cs
rename to src/libse/SubtitleFormats/FinalCutProXml17.cs
diff --git a/libse/SubtitleFormats/FinalCutProXml18.cs b/src/libse/SubtitleFormats/FinalCutProXml18.cs
similarity index 100%
rename from libse/SubtitleFormats/FinalCutProXml18.cs
rename to src/libse/SubtitleFormats/FinalCutProXml18.cs
diff --git a/libse/SubtitleFormats/FinalCutProXmlGap.cs b/src/libse/SubtitleFormats/FinalCutProXmlGap.cs
similarity index 98%
rename from libse/SubtitleFormats/FinalCutProXmlGap.cs
rename to src/libse/SubtitleFormats/FinalCutProXmlGap.cs
index 8f53f125c..9372fd441 100644
--- a/libse/SubtitleFormats/FinalCutProXmlGap.cs
+++ b/src/libse/SubtitleFormats/FinalCutProXmlGap.cs
@@ -1,149 +1,149 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class FinalCutProXmlGap : SubtitleFormat
- {
- public double FrameRate { get; set; }
-
- public override string Extension => ".fcpxml";
-
- public override string Name => "Final Cut Xml Gap";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- if (Configuration.Settings.General.CurrentFrameRate > 26)
- {
- FrameRate = 30;
- }
- else
- {
- FrameRate = 25;
- }
-
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- //
- //
- //
- //
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlClipStructure =
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " ";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
-
- XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/gap");
-
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode titleNode = xml.CreateElement("holder");
- titleNode.InnerXml = xmlClipStructure;
- titleNode = titleNode.SelectSingleNode("title");
- titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
- titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
- titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
- titleNode.SelectSingleNode("text").InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
- videoNode.AppendChild(titleNode);
- number++;
- }
-
- string xmlAsText = ToUtf8XmlString(xml);
- xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
- xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
- return xmlAsText;
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- FrameRate = Configuration.Settings.General.CurrentFrameRate;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(sb.ToString().Trim());
-
- foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/gap"))
- {
- try
- {
- foreach (XmlNode title in node.SelectNodes("title"))
- {
- var textNodes = title.SelectNodes("text");
- if (textNodes != null && textNodes.Count > 0)
- {
- var p = new Paragraph();
- p.StartTime = DecodeTime(title.Attributes["offset"]);
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds;
- var text = new StringBuilder();
- foreach (XmlNode textNode in textNodes)
- {
- text.AppendLine(textNode.InnerText);
- }
- p.Text = text.ToString().Trim();
- subtitle.Paragraphs.Add(p);
- }
- }
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- }
- }
-
- private static TimeCode DecodeTime(XmlAttribute duration)
- {
- // 220220/60000s
- if (duration != null)
- {
- var arr = duration.Value.TrimEnd('s').Split('/');
- if (arr.Length == 2)
- {
- return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
- }
-
- if (arr.Length == 1)
- {
- return TimeCode.FromSeconds(float.Parse(arr[0]));
- }
- }
- return new TimeCode();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class FinalCutProXmlGap : SubtitleFormat
+ {
+ public double FrameRate { get; set; }
+
+ public override string Extension => ".fcpxml";
+
+ public override string Name => "Final Cut Xml Gap";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ if (Configuration.Settings.General.CurrentFrameRate > 26)
+ {
+ FrameRate = 30;
+ }
+ else
+ {
+ FrameRate = 25;
+ }
+
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ //
+ //
+ //
+ //
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlClipStructure =
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " ";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+
+ XmlNode videoNode = xml.DocumentElement.SelectSingleNode("project/sequence/spine/gap");
+
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode titleNode = xml.CreateElement("holder");
+ titleNode.InnerXml = xmlClipStructure;
+ titleNode = titleNode.SelectSingleNode("title");
+ titleNode.Attributes["offset"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["name"].Value = HtmlUtil.RemoveHtmlTags(p.Text);
+ titleNode.Attributes["duration"].Value = Convert.ToInt64(p.Duration.TotalSeconds * 60000) + "/60000s";
+ titleNode.Attributes["start"].Value = Convert.ToInt64(p.StartTime.TotalSeconds * 60000) + "/60000s";
+ titleNode.SelectSingleNode("text").InnerText = HtmlUtil.RemoveHtmlTags(p.Text);
+ videoNode.AppendChild(titleNode);
+ number++;
+ }
+
+ string xmlAsText = ToUtf8XmlString(xml);
+ xmlAsText = xmlAsText.Replace("fcpxml[]", "fcpxml");
+ xmlAsText = xmlAsText.Replace("fcpxml []", "fcpxml");
+ return xmlAsText;
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ FrameRate = Configuration.Settings.General.CurrentFrameRate;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(sb.ToString().Trim());
+
+ foreach (XmlNode node in xml.SelectNodes("fcpxml/project/sequence/spine/gap"))
+ {
+ try
+ {
+ foreach (XmlNode title in node.SelectNodes("title"))
+ {
+ var textNodes = title.SelectNodes("text");
+ if (textNodes != null && textNodes.Count > 0)
+ {
+ var p = new Paragraph();
+ p.StartTime = DecodeTime(title.Attributes["offset"]);
+ p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + DecodeTime(title.Attributes["duration"]).TotalMilliseconds;
+ var text = new StringBuilder();
+ foreach (XmlNode textNode in textNodes)
+ {
+ text.AppendLine(textNode.InnerText);
+ }
+ p.Text = text.ToString().Trim();
+ subtitle.Paragraphs.Add(p);
+ }
+ }
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ }
+ }
+
+ private static TimeCode DecodeTime(XmlAttribute duration)
+ {
+ // 220220/60000s
+ if (duration != null)
+ {
+ var arr = duration.Value.TrimEnd('s').Split('/');
+ if (arr.Length == 2)
+ {
+ return TimeCode.FromSeconds(long.Parse(arr[0]) / double.Parse(arr[1]));
+ }
+
+ if (arr.Length == 1)
+ {
+ return TimeCode.FromSeconds(float.Parse(arr[0]));
+ }
+ }
+ return new TimeCode();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/FinalDraftTemplate2.cs b/src/libse/SubtitleFormats/FinalDraftTemplate2.cs
similarity index 100%
rename from libse/SubtitleFormats/FinalDraftTemplate2.cs
rename to src/libse/SubtitleFormats/FinalDraftTemplate2.cs
diff --git a/libse/SubtitleFormats/FlashXml.cs b/src/libse/SubtitleFormats/FlashXml.cs
similarity index 97%
rename from libse/SubtitleFormats/FlashXml.cs
rename to src/libse/SubtitleFormats/FlashXml.cs
index 96f1a02e9..d0dc7ae84 100644
--- a/libse/SubtitleFormats/FlashXml.cs
+++ b/src/libse/SubtitleFormats/FlashXml.cs
@@ -1,176 +1,176 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
-
- //
- //
- //
- //
This is fully skinnable through XML
using external images for each button]]>
- //
You can put in any order or enable/disable
the control buttons]]>
- //
Test below some of the customizable
properties this player has]]>
- //
Many other properties related to fonts, sizes, colors
and list properties are in style.css file]]>
- //
- //
- public class FlashXml : SubtitleFormat
- {
- public override string Extension => ".xml";
-
- public override string Name => "Flash Xml";
-
- public override bool IsMine(List lines, string fileName)
- {
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- string xmlAsString = sb.ToString().Trim();
- if ((xmlAsString.Contains("") || xmlAsString.Contains("")))
- {
- var xml = new XmlDocument { XmlResolver = null };
- try
- {
- xml.LoadXml(xmlAsString);
- var paragraphs = xml.DocumentElement.SelectNodes("div/p");
- return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "tt";
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- }
- }
- return false;
- }
-
- private static string ConvertToTimeString(TimeCode time)
- {
- return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}.{time.Milliseconds:00}";
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- "
" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument();
- xml.LoadXml(xmlStructure);
- XmlNode div = xml.DocumentElement.SelectSingleNode("div");
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- XmlNode paragraph = xml.CreateElement("p");
- string text = HtmlUtil.RemoveHtmlTags(p.Text, true);
-
- paragraph.InnerText = text;
- paragraph.InnerXml = "" + paragraph.InnerXml.Replace(Environment.NewLine, "
") + "]]>";
-
- XmlAttribute start = xml.CreateAttribute("begin");
- start.InnerText = ConvertToTimeString(p.StartTime);
- paragraph.Attributes.Append(start);
-
- XmlAttribute end = xml.CreateAttribute("end");
- end.InnerText = ConvertToTimeString(p.EndTime);
- paragraph.Attributes.Append(end);
-
- div.AppendChild(paragraph);
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
- double startSeconds = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(sb.ToString().Trim());
-
- var pText = new StringBuilder();
- foreach (XmlNode node in xml.DocumentElement.SelectNodes("div/p"))
- {
- try
- {
- foreach (XmlNode innerNode in node.ChildNodes)
- {
- if (innerNode.Name == "br")
- {
- pText.AppendLine();
- }
- else
- {
- pText.Append(innerNode.InnerText.Trim());
- }
- }
-
- var start = string.Empty;
- if (node.Attributes["begin"] != null)
- {
- start = node.Attributes["begin"].InnerText;
- }
-
- var end = string.Empty;
- if (node.Attributes["end"] != null)
- {
- end = node.Attributes["end"].InnerText;
- }
-
- var dur = string.Empty;
- if (node.Attributes["dur"] != null)
- {
- dur = node.Attributes["dur"].InnerText;
- }
-
- TimeCode startCode = TimeCode.FromSeconds(startSeconds);
- if (start.Length > 0)
- {
- startCode = GetTimeCode(start);
- }
-
- TimeCode endCode;
- if (end.Length > 0)
- {
- endCode = GetTimeCode(end);
- }
- else if (dur.Length > 0)
- {
- endCode = new TimeCode(GetTimeCode(dur).TotalMilliseconds + startCode.TotalMilliseconds);
- }
- else
- {
- endCode = new TimeCode(startCode.TotalMilliseconds + 3000);
- }
- startSeconds = endCode.TotalSeconds;
-
- subtitle.Paragraphs.Add(new Paragraph(startCode, endCode, pText.ToString().Replace("", string.Empty).Replace("", string.Empty)));
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.Message);
- _errorCount++;
- }
- pText.Clear();
- }
- subtitle.Renumber();
- }
-
- private static TimeCode GetTimeCode(string s)
- {
- if (s.EndsWith('s'))
- {
- s = s.TrimEnd('s');
- return TimeCode.FromSeconds(double.Parse(s));
- }
- var parts = s.Split(new[] { ':', '.', ',' });
- return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+
+ //
+ //
+ //
+ //
This is fully skinnable through XML
using external images for each button]]>
+ //
You can put in any order or enable/disable
the control buttons]]>
+ //
Test below some of the customizable
properties this player has]]>
+ //
Many other properties related to fonts, sizes, colors
and list properties are in style.css file]]>
+ //
+ //
+ public class FlashXml : SubtitleFormat
+ {
+ public override string Extension => ".xml";
+
+ public override string Name => "Flash Xml";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ string xmlAsString = sb.ToString().Trim();
+ if ((xmlAsString.Contains("") || xmlAsString.Contains("")))
+ {
+ var xml = new XmlDocument { XmlResolver = null };
+ try
+ {
+ xml.LoadXml(xmlAsString);
+ var paragraphs = xml.DocumentElement.SelectNodes("div/p");
+ return paragraphs != null && paragraphs.Count > 0 && xml.DocumentElement.Name == "tt";
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ }
+ }
+ return false;
+ }
+
+ private static string ConvertToTimeString(TimeCode time)
+ {
+ return $"{time.Hours:00}:{time.Minutes:00}:{time.Seconds:00}.{time.Milliseconds:00}";
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ "
" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument();
+ xml.LoadXml(xmlStructure);
+ XmlNode div = xml.DocumentElement.SelectSingleNode("div");
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ XmlNode paragraph = xml.CreateElement("p");
+ string text = HtmlUtil.RemoveHtmlTags(p.Text, true);
+
+ paragraph.InnerText = text;
+ paragraph.InnerXml = "" + paragraph.InnerXml.Replace(Environment.NewLine, "
") + "]]>";
+
+ XmlAttribute start = xml.CreateAttribute("begin");
+ start.InnerText = ConvertToTimeString(p.StartTime);
+ paragraph.Attributes.Append(start);
+
+ XmlAttribute end = xml.CreateAttribute("end");
+ end.InnerText = ConvertToTimeString(p.EndTime);
+ paragraph.Attributes.Append(end);
+
+ div.AppendChild(paragraph);
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+ double startSeconds = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(sb.ToString().Trim());
+
+ var pText = new StringBuilder();
+ foreach (XmlNode node in xml.DocumentElement.SelectNodes("div/p"))
+ {
+ try
+ {
+ foreach (XmlNode innerNode in node.ChildNodes)
+ {
+ if (innerNode.Name == "br")
+ {
+ pText.AppendLine();
+ }
+ else
+ {
+ pText.Append(innerNode.InnerText.Trim());
+ }
+ }
+
+ var start = string.Empty;
+ if (node.Attributes["begin"] != null)
+ {
+ start = node.Attributes["begin"].InnerText;
+ }
+
+ var end = string.Empty;
+ if (node.Attributes["end"] != null)
+ {
+ end = node.Attributes["end"].InnerText;
+ }
+
+ var dur = string.Empty;
+ if (node.Attributes["dur"] != null)
+ {
+ dur = node.Attributes["dur"].InnerText;
+ }
+
+ TimeCode startCode = TimeCode.FromSeconds(startSeconds);
+ if (start.Length > 0)
+ {
+ startCode = GetTimeCode(start);
+ }
+
+ TimeCode endCode;
+ if (end.Length > 0)
+ {
+ endCode = GetTimeCode(end);
+ }
+ else if (dur.Length > 0)
+ {
+ endCode = new TimeCode(GetTimeCode(dur).TotalMilliseconds + startCode.TotalMilliseconds);
+ }
+ else
+ {
+ endCode = new TimeCode(startCode.TotalMilliseconds + 3000);
+ }
+ startSeconds = endCode.TotalSeconds;
+
+ subtitle.Paragraphs.Add(new Paragraph(startCode, endCode, pText.ToString().Replace("", string.Empty).Replace("", string.Empty)));
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine(ex.Message);
+ _errorCount++;
+ }
+ pText.Clear();
+ }
+ subtitle.Renumber();
+ }
+
+ private static TimeCode GetTimeCode(string s)
+ {
+ if (s.EndsWith('s'))
+ {
+ s = s.TrimEnd('s');
+ return TimeCode.FromSeconds(double.Parse(s));
+ }
+ var parts = s.Split(new[] { ':', '.', ',' });
+ return new TimeCode(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]), int.Parse(parts[3]));
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/Footage.cs b/src/libse/SubtitleFormats/Footage.cs
similarity index 97%
rename from libse/SubtitleFormats/Footage.cs
rename to src/libse/SubtitleFormats/Footage.cs
index a9ca7167a..3cf651803 100644
--- a/libse/SubtitleFormats/Footage.cs
+++ b/src/libse/SubtitleFormats/Footage.cs
@@ -1,170 +1,170 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class Footage : SubtitleFormat
- {
- private static readonly Regex RegexTimeCode = new Regex(@"^\s*\d+,\d\d$", RegexOptions.Compiled);
-
- private enum ExpectingLine
- {
- Number,
- TimeStart,
- TimeEnd,
- Text
- }
-
- public override string Extension => ".txt";
-
- public override string Name => "Footage";
-
- public override bool IsMine(List lines, string fileName)
- {
- var asc = new TimeLineFootageAscii();
- if (fileName != null && asc.IsMine(null, fileName))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- //1.
- // 66,13
- // 70,00
- //#Tā nu es sapazinos
- //#И так я познакомился
-
- //2.
- // 71,14
- // 78,10
- //#ar dakteri Henriju Gūsu.
- //#с доктором Генри Гусом.
-
- const string paragraphWriteFormat = "{4}.{3}{0}{3}{1}{3}{2}{3}";
- var sb = new StringBuilder();
- int count = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- count++;
- string text = HtmlUtil.RemoveHtmlTags(p.Text);
- if (p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal))
- {
- text = "#" + text;
- }
-
- sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine, count));
- }
- return sb.ToString().Trim();
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- Paragraph paragraph = null;
- ExpectingLine expecting = ExpectingLine.Number;
- _errorCount = 0;
-
- subtitle.Paragraphs.Clear();
- char[] splitChar = { ',' };
- foreach (string line in lines)
- {
- if (line.EndsWith('.') && Utilities.IsInteger(line.TrimEnd('.')))
- {
- if (!string.IsNullOrEmpty(paragraph?.Text))
- {
- subtitle.Paragraphs.Add(paragraph);
- }
-
- paragraph = new Paragraph();
- expecting = ExpectingLine.TimeStart;
- }
- else if (paragraph != null && expecting == ExpectingLine.TimeStart && RegexTimeCode.IsMatch(line))
- {
- string[] parts = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 2)
- {
- try
- {
- var tc = DecodeTimeCode(parts);
- paragraph.StartTime = tc;
- expecting = ExpectingLine.TimeEnd;
- }
- catch
- {
- _errorCount++;
- expecting = ExpectingLine.Number;
- }
- }
- }
- else if (paragraph != null && expecting == ExpectingLine.TimeEnd && RegexTimeCode.IsMatch(line))
- {
- string[] parts = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length == 2)
- {
- try
- {
- var tc = DecodeTimeCode(parts);
- paragraph.EndTime = tc;
- expecting = ExpectingLine.Text;
- }
- catch
- {
- _errorCount++;
- expecting = ExpectingLine.Number;
- }
- }
- }
- else
- {
- if (paragraph != null && expecting == ExpectingLine.Text)
- {
- if (line.Length > 0)
- {
- string s = line.Trim();
- if (s.StartsWith('#'))
- {
- s = "" + s.Remove(0, 1) + "";
- }
-
- paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim();
- paragraph.Text = paragraph.Text.Replace("" + Environment.NewLine + "", Environment.NewLine);
- if (paragraph.Text.Length > 2000)
- {
- _errorCount += 100;
- return;
- }
- }
- }
- }
- }
- if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text))
- {
- subtitle.Paragraphs.Add(paragraph);
- }
-
- subtitle.Renumber();
- }
-
- private static string EncodeTimeCode(TimeCode time)
- {
- int frames = MillisecondsToFrames(time.TotalMilliseconds);
- int footage = frames / 16;
- int rest = (int)Math.Round(frames % 16.0 / 16.0 * 24.0);
- return $"{footage:00},{rest:00}".PadLeft(8);
- }
-
- private static TimeCode DecodeTimeCode(string[] parts)
- {
- int frames16 = int.Parse(parts[0]);
- int frames = int.Parse(parts[1]);
- return new TimeCode(FramesToMilliseconds(16 * frames16 + (frames * 16.0 / 24.0)));
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Footage : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCode = new Regex(@"^\s*\d+,\d\d$", RegexOptions.Compiled);
+
+ private enum ExpectingLine
+ {
+ Number,
+ TimeStart,
+ TimeEnd,
+ Text
+ }
+
+ public override string Extension => ".txt";
+
+ public override string Name => "Footage";
+
+ public override bool IsMine(List lines, string fileName)
+ {
+ var asc = new TimeLineFootageAscii();
+ if (fileName != null && asc.IsMine(null, fileName))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ //1.
+ // 66,13
+ // 70,00
+ //#Tā nu es sapazinos
+ //#И так я познакомился
+
+ //2.
+ // 71,14
+ // 78,10
+ //#ar dakteri Henriju Gūsu.
+ //#с доктором Генри Гусом.
+
+ const string paragraphWriteFormat = "{4}.{3}{0}{3}{1}{3}{2}{3}";
+ var sb = new StringBuilder();
+ int count = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ count++;
+ string text = HtmlUtil.RemoveHtmlTags(p.Text);
+ if (p.Text.StartsWith("", StringComparison.Ordinal) && p.Text.EndsWith("", StringComparison.Ordinal))
+ {
+ text = "#" + text;
+ }
+
+ sb.AppendLine(string.Format(paragraphWriteFormat, EncodeTimeCode(p.StartTime), EncodeTimeCode(p.EndTime), text, Environment.NewLine, count));
+ }
+ return sb.ToString().Trim();
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ Paragraph paragraph = null;
+ ExpectingLine expecting = ExpectingLine.Number;
+ _errorCount = 0;
+
+ subtitle.Paragraphs.Clear();
+ char[] splitChar = { ',' };
+ foreach (string line in lines)
+ {
+ if (line.EndsWith('.') && Utilities.IsInteger(line.TrimEnd('.')))
+ {
+ if (!string.IsNullOrEmpty(paragraph?.Text))
+ {
+ subtitle.Paragraphs.Add(paragraph);
+ }
+
+ paragraph = new Paragraph();
+ expecting = ExpectingLine.TimeStart;
+ }
+ else if (paragraph != null && expecting == ExpectingLine.TimeStart && RegexTimeCode.IsMatch(line))
+ {
+ string[] parts = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2)
+ {
+ try
+ {
+ var tc = DecodeTimeCode(parts);
+ paragraph.StartTime = tc;
+ expecting = ExpectingLine.TimeEnd;
+ }
+ catch
+ {
+ _errorCount++;
+ expecting = ExpectingLine.Number;
+ }
+ }
+ }
+ else if (paragraph != null && expecting == ExpectingLine.TimeEnd && RegexTimeCode.IsMatch(line))
+ {
+ string[] parts = line.Split(splitChar, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2)
+ {
+ try
+ {
+ var tc = DecodeTimeCode(parts);
+ paragraph.EndTime = tc;
+ expecting = ExpectingLine.Text;
+ }
+ catch
+ {
+ _errorCount++;
+ expecting = ExpectingLine.Number;
+ }
+ }
+ }
+ else
+ {
+ if (paragraph != null && expecting == ExpectingLine.Text)
+ {
+ if (line.Length > 0)
+ {
+ string s = line.Trim();
+ if (s.StartsWith('#'))
+ {
+ s = "" + s.Remove(0, 1) + "";
+ }
+
+ paragraph.Text = (paragraph.Text + Environment.NewLine + s).Trim();
+ paragraph.Text = paragraph.Text.Replace("" + Environment.NewLine + "", Environment.NewLine);
+ if (paragraph.Text.Length > 2000)
+ {
+ _errorCount += 100;
+ return;
+ }
+ }
+ }
+ }
+ }
+ if (paragraph != null && !string.IsNullOrEmpty(paragraph.Text))
+ {
+ subtitle.Paragraphs.Add(paragraph);
+ }
+
+ subtitle.Renumber();
+ }
+
+ private static string EncodeTimeCode(TimeCode time)
+ {
+ int frames = MillisecondsToFrames(time.TotalMilliseconds);
+ int footage = frames / 16;
+ int rest = (int)Math.Round(frames % 16.0 / 16.0 * 24.0);
+ return $"{footage:00},{rest:00}".PadLeft(8);
+ }
+
+ private static TimeCode DecodeTimeCode(string[] parts)
+ {
+ int frames16 = int.Parse(parts[0]);
+ int frames = int.Parse(parts[1]);
+ return new TimeCode(FramesToMilliseconds(16 * frames16 + (frames * 16.0 / 24.0)));
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/GooglePlayJson.cs b/src/libse/SubtitleFormats/GooglePlayJson.cs
similarity index 100%
rename from libse/SubtitleFormats/GooglePlayJson.cs
rename to src/libse/SubtitleFormats/GooglePlayJson.cs
diff --git a/libse/SubtitleFormats/GpacTtxt.cs b/src/libse/SubtitleFormats/GpacTtxt.cs
similarity index 97%
rename from libse/SubtitleFormats/GpacTtxt.cs
rename to src/libse/SubtitleFormats/GpacTtxt.cs
index 65da69587..3e3dd2b9f 100644
--- a/libse/SubtitleFormats/GpacTtxt.cs
+++ b/src/libse/SubtitleFormats/GpacTtxt.cs
@@ -1,120 +1,120 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- public class GpacTtxt : SubtitleFormat
- {
- public override string Extension => ".ttxt";
-
- public override string Name => "GPAC TTXT";
-
- public override string ToText(Subtitle subtitle, string title)
- {
- string xmlStructure =
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "";
-
- string xmlTrackStructure =
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- " 00:03:57:16" + Environment.NewLine +
- "";
-
- var xml = new XmlDocument { XmlResolver = null };
- xml.LoadXml(xmlStructure);
- // TODO: Set variables...
- XmlNode trackNode = xml.DocumentElement.SelectSingleNode("TrackList/Track/StItemList");
-
- int number = 1;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- // starttime + text
- XmlNode stItem = xml.CreateElement("StItem");
- stItem.InnerXml = xmlTrackStructure;
-
- XmlAttribute memo = xml.CreateAttribute("Memo");
- memo.InnerText = string.Empty;
- stItem.Attributes.Append(memo);
-
- XmlAttribute tc = xml.CreateAttribute("TC");
- tc.InnerText = ((int)Math.Round(p.StartTime.TotalMilliseconds)).ToString();
- stItem.Attributes.Append(tc);
-
- XmlAttribute row = xml.CreateAttribute("Row");
- row.InnerText = number.ToString();
- stItem.Attributes.Append(row);
-
- XmlNodeList list = stItem.SelectNodes("StTextList/StText");
- list[0].InnerText = p.Text;
- list[2].InnerText = p.StartTime.ToHHMMSSFF();
- trackNode.AppendChild(stItem);
- number++;
-
- // endtime
- stItem = xml.CreateElement("StItem");
- stItem.InnerXml = xmlTrackStructure;
-
- memo = xml.CreateAttribute("Memo");
- memo.InnerText = string.Empty;
- stItem.Attributes.Append(memo);
-
- tc = xml.CreateAttribute("TC");
- tc.InnerText = ((int)Math.Round(p.EndTime.TotalMilliseconds)).ToString();
- stItem.Attributes.Append(tc);
-
- row = xml.CreateAttribute("Row");
- row.InnerText = number.ToString();
- stItem.Attributes.Append(row);
-
- list = stItem.SelectNodes("StTextList/StText");
- list[0].InnerText = string.Empty;
- list[2].InnerText = p.EndTime.ToString();
- trackNode.AppendChild(stItem);
- number++;
- }
-
- return ToUtf8XmlString(xml);
- }
-
- public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
- {
- _errorCount = 0;
-
- var sb = new StringBuilder();
- lines.ForEach(line => sb.AppendLine(line));
- if (!sb.ToString().Contains(" 1)
- {
- text = (list[0].InnerText + Environment.NewLine + list[1].InnerText).Trim();
- }
- else if (list.Count == 1)
- {
- text = list[0].InnerText.Trim();
- }
-
- p.Text = text;
- subtitle.Paragraphs.Add(p);
- }
- catch
- {
- _errorCount++;
- }
- }
- subtitle.Renumber();
- }
- catch
- {
- _errorCount = 1;
- return;
- }
-
- int i = 0;
- foreach (Paragraph p in subtitle.Paragraphs)
- {
- i++;
- var next = subtitle.GetParagraphOrDefault(i);
- if (next != null)
- {
- p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds;
- }
- }
- subtitle.RemoveEmptyLines();
- }
-
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class IssXml : SubtitleFormat
+ {
+ private static readonly Regex RegexTimeCodes = new Regex(@"^\d\d:\d\d:\d\d:\d\d$", RegexOptions.Compiled); //00:02:56:02
+
+ public override string Extension => ".ats";
+
+ public override string Name => "ATS ISS";
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ string xmlStructure =
+ "" + Environment.NewLine +
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ "";
+
+ string xmlTrackStructure =
+ "" + Environment.NewLine +
+ " " + Environment.NewLine +
+ " " + Environment.NewLine +
+ " 00:03:57:16" + Environment.NewLine +
+ "";
+
+ var xml = new XmlDocument { XmlResolver = null };
+ xml.LoadXml(xmlStructure);
+ // TODO: Set variables...
+ XmlNode trackNode = xml.DocumentElement.SelectSingleNode("TrackList/Track/StItemList");
+
+ int number = 1;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ // starttime + text
+ XmlNode stItem = xml.CreateElement("StItem");
+ stItem.InnerXml = xmlTrackStructure;
+
+ XmlAttribute memo = xml.CreateAttribute("Memo");
+ memo.InnerText = string.Empty;
+ stItem.Attributes.Append(memo);
+
+ XmlAttribute tc = xml.CreateAttribute("TC");
+ tc.InnerText = ((int)Math.Round(p.StartTime.TotalMilliseconds)).ToString();
+ stItem.Attributes.Append(tc);
+
+ XmlAttribute row = xml.CreateAttribute("Row");
+ row.InnerText = number.ToString();
+ stItem.Attributes.Append(row);
+
+ XmlNodeList list = stItem.SelectNodes("StTextList/StText");
+ list[0].InnerText = p.Text;
+ list[2].InnerText = p.StartTime.ToHHMMSSFF();
+ trackNode.AppendChild(stItem);
+ number++;
+
+ // endtime
+ stItem = xml.CreateElement("StItem");
+ stItem.InnerXml = xmlTrackStructure;
+
+ memo = xml.CreateAttribute("Memo");
+ memo.InnerText = string.Empty;
+ stItem.Attributes.Append(memo);
+
+ tc = xml.CreateAttribute("TC");
+ tc.InnerText = ((int)Math.Round(p.EndTime.TotalMilliseconds)).ToString();
+ stItem.Attributes.Append(tc);
+
+ row = xml.CreateAttribute("Row");
+ row.InnerText = number.ToString();
+ stItem.Attributes.Append(row);
+
+ list = stItem.SelectNodes("StTextList/StText");
+ list[0].InnerText = string.Empty;
+ list[2].InnerText = p.EndTime.ToString();
+ trackNode.AppendChild(stItem);
+ number++;
+ }
+
+ return ToUtf8XmlString(xml);
+ }
+
+ public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName)
+ {
+ _errorCount = 0;
+
+ var sb = new StringBuilder();
+ lines.ForEach(line => sb.AppendLine(line));
+ if (!sb.ToString().Contains(" 1)
+ {
+ text = (list[0].InnerText + Environment.NewLine + list[1].InnerText).Trim();
+ }
+ else if (list.Count == 1)
+ {
+ text = list[0].InnerText.Trim();
+ }
+
+ p.Text = text;
+ subtitle.Paragraphs.Add(p);
+ }
+ catch
+ {
+ _errorCount++;
+ }
+ }
+ subtitle.Renumber();
+ }
+ catch
+ {
+ _errorCount = 1;
+ return;
+ }
+
+ int i = 0;
+ foreach (Paragraph p in subtitle.Paragraphs)
+ {
+ i++;
+ var next = subtitle.GetParagraphOrDefault(i);
+ if (next != null)
+ {
+ p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds;
+ }
+ }
+ subtitle.RemoveEmptyLines();
+ }
+
+ }
+}
diff --git a/libse/SubtitleFormats/ItunesTimedText.cs b/src/libse/SubtitleFormats/ItunesTimedText.cs
similarity index 97%
rename from libse/SubtitleFormats/ItunesTimedText.cs
rename to src/libse/SubtitleFormats/ItunesTimedText.cs
index 020307e21..3496ad4bc 100644
--- a/libse/SubtitleFormats/ItunesTimedText.cs
+++ b/src/libse/SubtitleFormats/ItunesTimedText.cs
@@ -1,377 +1,377 @@
-using System;
-using System.Collections.Generic;
-using System.Xml;
-using Nikse.SubtitleEdit.Core.Common;
-
-namespace Nikse.SubtitleEdit.Core.SubtitleFormats
-{
- ///
- /// Crappy format... should always be saved as UTF-8 without BOM (hacked Main.cs) and
tags should be oldstyle
- ///
- public class ItunesTimedText : TimedText10
- {
- public override string Extension => ".itt";
-
- public new const string NameOfFormat = "iTunes Timed Text";
-
- public override string Name => NameOfFormat;
-
- public override bool IsMine(List lines, string fileName)
- {
- if (fileName != null && !fileName.EndsWith(Extension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (new NetflixTimedText().IsMine(lines, fileName))
- {
- return false;
- }
-
- return base.IsMine(lines, fileName);
- }
-
- public override string ToText(Subtitle subtitle, string title)
- {
- XmlNode styleHead = null;
- bool convertedFromSubStationAlpha = false;
- if (subtitle.Header != null)
- {
- try
- {
- var x = new XmlDocument();
- x.LoadXml(subtitle.Header);
- var xnsmgr = new XmlNamespaceManager(x.NameTable);
- xnsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
- styleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr);
- }
- catch
- {
- styleHead = null;
- }
- if (styleHead == null && (subtitle.Header.Contains("[V4+ Styles]") || subtitle.Header.Contains("[V4 Styles]")))
- {
- var x = new XmlDocument();
- x.LoadXml(new ItunesTimedText().ToText(new Subtitle(), "tt")); // load default xml
- var xnsmgr = new XmlNamespaceManager(x.NameTable);
- xnsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
- styleHead = x.DocumentElement.SelectSingleNode("ttml:head", xnsmgr);
- styleHead.SelectSingleNode("ttml:styling", xnsmgr).RemoveAll();
- foreach (string styleName in AdvancedSubStationAlpha.GetStylesFromHeader(subtitle.Header))
- {
- try
- {
- var ssaStyle = AdvancedSubStationAlpha.GetSsaStyle(styleName, subtitle.Header);
-
- string fontStyle = "normal";
- if (ssaStyle.Italic)
- {
- fontStyle = "italic";
- }
-
- string fontWeight = "normal";
- if (ssaStyle.Bold)
- {
- fontWeight = "bold";
- }
-
- AddStyleToXml(x, styleHead, xnsmgr, ssaStyle.Name, ssaStyle.FontName, fontWeight, fontStyle, Utilities.ColorToHex(ssaStyle.Primary), ssaStyle.FontSize.ToString());
- convertedFromSubStationAlpha = true;
-
- }
- catch
- {
- // ignored
- }
- }
- subtitle.Header = x.OuterXml; // save new xml with styles in header
- }
- }
-
- var xml = new XmlDocument { XmlResolver = null };
- var nsmgr = new XmlNamespaceManager(xml.NameTable);
- nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
- nsmgr.AddNamespace("ttp", "http://www.w3.org/ns/10/ttml#parameter");
- nsmgr.AddNamespace("tts", "http://www.w3.org/ns/ttml#styling");
- nsmgr.AddNamespace("ttm", "http://www.w3.org/ns/10/ttml#metadata");
-
- string frameRate = ((int)Math.Round(Configuration.Settings.General.CurrentFrameRate)).ToString();
- string frameRateMultiplier = "999 1000";
- if (Configuration.Settings.General.CurrentFrameRate % 1.0 < 0.01)
- {
- frameRateMultiplier = "1 1";
- }
- string dropMode = "nonDrop";
- if (Math.Abs(Configuration.Settings.General.CurrentFrameRate - 29.97) < 0.01)
- {
- dropMode = "dropNTSC";
- }
-
- const string language = "en-US";
- string xmlStructure = "" + Environment.NewLine +
- "" + Environment.NewLine +
- " " + Environment.NewLine +
- " " + Environment.NewLine +
- "
-
-
-<-- Open play menu, choose Captions and Subtiles, On if available -->
-<-- Open tools menu, Security, Show local captions when present -->";
-
- bool useExtra = false;
- if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.StartsWith("", StringComparison.Ordinal);
- if (styleEnd > 0)
- {
- subtitle.Header = allInput.Substring(styleStart, styleEnd - styleStart + 8);
- }
- }
-
- const string syncTag = "= 0)
- {
- string millisecondsAsString = string.Empty;
- while (index < allInput.Length && expectedChars.Contains(allInput[index]))
- {
- if (allInput[index] != '"' && allInput[index] != '\'')
- {
- millisecondsAsString += allInput[index];
- }
-
- index++;
- }
-
- while (index < allInput.Length && allInput[index] != '>')
- {
- index++;
- }
-
- if (index < allInput.Length && allInput[index] == '>')
- {
- index++;
- }
-
- int syncEndPos = allInputLower.IndexOf(syncTag, index, StringComparison.Ordinal);
- if (hasEncryptedTags)
- {
- int syncEndPosEnc = allInputLower.IndexOf(syncTagEnc, index, StringComparison.Ordinal);
- if (syncStartPosEnc >= 0 && syncStartPosEnc < syncStartPos || syncEndPos == -1)
- {
- syncEndPos = syncEndPosEnc;
- }
- }
-
- string text;
- if (syncEndPos >= 0)
- {
- text = allInput.Substring(index, syncEndPos - index);
- }
- else
- {
- text = allInput.Substring(index);
- }
-
- string textToLower = text.ToLowerInvariant();
- if (textToLower.Contains(" class="))
- {
- var className = new StringBuilder();
- int startClass = textToLower.IndexOf(" class=", StringComparison.Ordinal);
- int indexClass = startClass + 7;
- while (indexClass < textToLower.Length && (Utilities.LowercaseLettersWithNumbers + @"'""").Contains(textToLower[indexClass]))
- {
- className.Append(text[indexClass]);
- indexClass++;
- }
- p.Extra = className.ToString().Trim(' ', '\'', '"');
- }
-
- if (text.Contains("ID=\"Source\"") || text.Contains("ID=Source"))
- {
- int sourceIndex = text.IndexOf("ID=\"Source\"", StringComparison.Ordinal);
- if (sourceIndex < 0)
- {
- sourceIndex = text.IndexOf("ID=Source", StringComparison.Ordinal);
- }
-
- int st = sourceIndex - 1;
- while (st > 0 && text.Substring(st, 2).ToUpperInvariant() != " 0)
- {
- text = text.Substring(0, st) + text.Substring(sourceIndex);
- }
- int et = st;
- while (et < text.Length - 5 && text.Substring(et, 3).ToUpperInvariant() != "
" && text.Substring(et, 4).ToUpperInvariant() != "
")
- {
- et++;
- }
- text = text.Substring(0, st) + text.Substring(et);
- }
- text = text.Replace(Environment.NewLine, " ");
- text = text.Replace(" ", " ");
-
- text = text.TrimEnd();
- text = Regex.Replace(text, @"
", Environment.NewLine, RegexOptions.IgnoreCase);
-
- while (text.Contains(" "))
- {
- text = text.Replace(" ", " ");
- }
-
- text = text.Replace("", string.Empty).Replace("", string.Empty).TrimEnd();
- text = text.Replace("", string.Empty).Replace("", string.Empty).TrimEnd();
-
- int endSyncPos = text.ToUpperInvariant().IndexOf("", StringComparison.OrdinalIgnoreCase);
- if (text.IndexOf('>') > 0 && (text.IndexOf('>') < endSyncPos || endSyncPos == -1))
- {
- text = text.Remove(0, text.IndexOf('>') + 1);
- }
-
- text = text.TrimEnd();
-
- if (text.EndsWith("", StringComparison.OrdinalIgnoreCase))
- {
- text = text.Substring(0, text.Length - 7).TrimEnd();
- }
-
- if (text.EndsWith("
", StringComparison.Ordinal) || text.EndsWith("", StringComparison.OrdinalIgnoreCase))
- {
- text = text.Substring(0, text.Length - 4).TrimEnd();
- }
-
- text = RemoveDiv(text).Trim();
- text = text.Replace(" ", " ").Replace("&NBSP;", " ");
- text = text.Replace("", string.Empty).Replace("", string.Empty).Replace("", string.Empty);
- if (string.IsNullOrWhiteSpace(text))
- {
- text = string.Empty;
- }
-
- if (text.Contains(""))
- {
- text += "";
- }
-
- if (text.StartsWith("") && !text.Contains(""))
- {
- text += "";
- }
-
- if (text.Contains('<') && text.Contains('>'))
- {
- var total = new StringBuilder();
- var partial = new StringBuilder();
- bool tagOn = false;
- for (int i = 0; i < text.Length && i < 999; i++)
- {
- string tmp = text.Substring(i);
- if (tmp.StartsWith('<') &&
- (tmp.StartsWith("') && tagOn)
- {
- tagOn = false;
- total.Append('>');
- }
- else if (!tagOn)
- {
- partial.Append(text[i]);
- }
- else
- {
- total.Append(text[i]);
- }
- }
- total.Append(WebUtility.HtmlDecode(partial.ToString()));
- text = total.ToString();
- }
- else
- {
- text = WebUtility.HtmlDecode(text);
- }
-
- var cleanText = text.FixExtraSpaces();
- cleanText = cleanText.Trim();
-
- if (!string.IsNullOrEmpty(p.Text) && !string.IsNullOrEmpty(millisecondsAsString))
- {
- p.EndTime = new TimeCode(long.Parse(millisecondsAsString));
- subtitle.Paragraphs.Add(p);
- p = new Paragraph();
- }
-
- p.Text = cleanText;
- long l;
- if (long.TryParse(millisecondsAsString, out l))
- {
- p.StartTime = new TimeCode(l);
- }
-
- if (syncEndPos <= 0)
- {
- syncStartPos = -1;
- }
- else
- {
- syncStartPos = allInputLower.IndexOf(syncTag, syncEndPos, StringComparison.Ordinal);
- index = syncStartPos + syncTag.Length;
-
- if (hasEncryptedTags)
- {
- syncStartPosEnc = allInputLower.IndexOf(syncTagEnc, syncEndPos, StringComparison.Ordinal);
- if (syncStartPosEnc >= 0 && syncStartPosEnc < syncStartPos || syncStartPos == -1)
- {
- syncStartPos = syncStartPosEnc;
- index = syncStartPosEnc + syncTagEnc.Length;
- }
- }
- }
- }
- if (!string.IsNullOrEmpty(p.Text) && !subtitle.Paragraphs.Contains(p))
- {
- p.EndTime.TotalMilliseconds = p.StartTime.TotalMilliseconds + Utilities.GetOptimalDisplayMilliseconds(p.Text);
- subtitle.Paragraphs.Add(p);
- }
- subtitle.Renumber();
-
- if (subtitle.Paragraphs.Count > 0 &&
- (subtitle.Paragraphs[subtitle.Paragraphs.Count - 1].Text.ToUpperInvariant().Trim() == ""))
- {
- subtitle.Paragraphs.RemoveAt(subtitle.Paragraphs.Count - 1);
- }
-
- foreach (Paragraph p2 in subtitle.Paragraphs)
- {
- p2.Text = p2.Text.Replace('\u00A0', ' '); // non-breaking space to normal space
- }
- }
-
- private string RemoveDiv(string text)
- {
- int indexOfDiv = text.IndexOf("", StringComparison.Ordinal);
- }
-
- int maxLoop = 10;
- while (indexOfDiv > 0 && maxLoop >= 0)
- {
- int indexOfStartEnd = text.IndexOf(">", indexOfDiv + 1, StringComparison.Ordinal);
- if (indexOfStartEnd > 0)
- {
- text = text.Remove(indexOfDiv, indexOfStartEnd - indexOfDiv + 1);
- text = text.Replace("
", string.Empty);
-
- indexOfDiv = text.IndexOf("", StringComparison.Ordinal);
- }
- }
- maxLoop--;
- }
- return text;
- }
-
- public override bool HasStyleSupport => true;
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Text;
+using System.Text.RegularExpressions;
+using Nikse.SubtitleEdit.Core.Common;
+
+namespace Nikse.SubtitleEdit.Core.SubtitleFormats
+{
+ public class Sami : SubtitleFormat
+ {
+ public override string Extension => ".smi";
+
+ public override string Name => "SAMI";
+
+ public override bool IsMine(List
lines, string fileName)
+ {
+ var sb = new StringBuilder();
+ foreach (string l in lines)
+ {
+ sb.AppendLine(l);
+ }
+
+ if (Name == "SAMI" && sb.ToString().Contains(""))
+ {
+ return false;
+ }
+
+ return base.IsMine(lines, fileName);
+ }
+
+ public override string ToText(Subtitle subtitle, string title)
+ {
+ var language = LanguageAutoDetect.AutoDetectGoogleLanguage(subtitle);
+ var ci = CultureInfo.GetCultureInfo(language);
+ language = CultureInfo.CreateSpecificCulture(ci.Name).Name;
+ string languageTag = $"{language.Replace("-", string.Empty).ToUpperInvariant()}CC";
+ string languageName = ci.EnglishName;
+ string languageStyle = $".{languageTag} [ name: {languageName}; lang: {language.Replace("_", "-")} ; SAMIType: CC ; ]";
+ languageStyle = languageStyle.Replace("[", "{").Replace("]", "}");
+
+ string header =
+@"
+
+_TITLE_
+
+ Metrics {time:ms;}
+ Spec {MSFT:1.0;}
+
+
+
+
+<-- Open play menu, choose Captions and Subtiles, On if available -->
+<-- Open tools menu, Security, Show local captions when present -->";
+
+ bool useExtra = false;
+ if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.StartsWith("", StringComparison.Ordinal);
+ if (styleEnd > 0)
+ {
+ subtitle.Header = allInput.Substring(styleStart, styleEnd - styleStart + 8);
+ }
+ }
+
+ const string syncTag = "= 0)
+ {
+ string millisecondsAsString = string.Empty;
+ while (index < allInput.Length && expectedChars.Contains(allInput[index]))
+ {
+ if (allInput[index] != '"' && allInput[index] != '\'')
+ {
+ millisecondsAsString += allInput[index];
+ }
+
+ index++;
+ }
+
+ while (index < allInput.Length && allInput[index] != '>')
+ {
+ index++;
+ }
+
+ if (index < allInput.Length && allInput[index] == '>')
+ {
+ index++;
+ }
+
+ int syncEndPos = allInputLower.IndexOf(syncTag, index, StringComparison.Ordinal);
+ if (hasEncryptedTags)
+ {
+ int syncEndPosEnc = allInputLower.IndexOf(syncTagEnc, index, StringComparison.Ordinal);
+ if (syncStartPosEnc >= 0 && syncStartPosEnc < syncStartPos || syncEndPos == -1)
+ {
+ syncEndPos = syncEndPosEnc;
+ }
+ }
+
+ string text;
+ if (syncEndPos >= 0)
+ {
+ text = allInput.Substring(index, syncEndPos - index);
+ }
+ else
+ {
+ text = allInput.Substring(index);
+ }
+
+ string textToLower = text.ToLowerInvariant();
+ if (textToLower.Contains(" class="))
+ {
+ var className = new StringBuilder();
+ int startClass = textToLower.IndexOf(" class=", StringComparison.Ordinal);
+ int indexClass = startClass + 7;
+ while (indexClass < textToLower.Length && (Utilities.LowercaseLettersWithNumbers + @"'""").Contains(textToLower[indexClass]))
+ {
+ className.Append(text[indexClass]);
+ indexClass++;
+ }
+ p.Extra = className.ToString().Trim(' ', '\'', '"');
+ }
+
+ if (text.Contains("ID=\"Source\"") || text.Contains("ID=Source"))
+ {
+ int sourceIndex = text.IndexOf("ID=\"Source\"", StringComparison.Ordinal);
+ if (sourceIndex < 0)
+ {
+ sourceIndex = text.IndexOf("ID=Source", StringComparison.Ordinal);
+ }
+
+ int st = sourceIndex - 1;
+ while (st > 0 && text.Substring(st, 2).ToUpperInvariant() != " 0)
+ {
+ text = text.Substring(0, st) + text.Substring(sourceIndex);
+ }
+ int et = st;
+ while (et < text.Length - 5 && text.Substring(et, 3).ToUpperInvariant() != "
" && text.Substring(et, 4).ToUpperInvariant() != "
")
+ {
+ et++;
+ }
+ text = text.Substring(0, st) + text.Substring(et);
+ }
+ text = text.Replace(Environment.NewLine, " ");
+ text = text.Replace(" ", " ");
+
+ text = text.TrimEnd();
+ text = Regex.Replace(text, @"
", Environment.NewLine, RegexOptions.IgnoreCase);
+
+ while (text.Contains(" "))
+ {
+ text = text.Replace(" ", " ");
+ }
+
+ text = text.Replace("", string.Empty).Replace("", string.Empty).TrimEnd();
+ text = text.Replace("