├── .gitattributes ├── .gitignore ├── HwpSharp.sln ├── LICENSE ├── README.md ├── global.json ├── src └── HwpSharp │ ├── Common │ ├── Exceptions.cs │ ├── IBodyText.cs │ ├── IDocumentInformation.cs │ ├── IFileHeader.cs │ ├── IHwpDocument.cs │ └── ISummaryInformation.cs │ ├── Hwp5 │ ├── BodyText │ │ ├── BodyText.cs │ │ ├── DataRecords │ │ │ ├── ParagraphHeader.cs │ │ │ └── ParagraphText.cs │ │ └── Section.cs │ ├── Document.cs │ ├── DocumentInformation │ │ ├── DataRecords │ │ │ ├── BinData.cs │ │ │ ├── BorderFill.cs │ │ │ ├── DocumentProperty.cs │ │ │ ├── FaceName.cs │ │ │ └── IdMapping.cs │ │ └── DocumentInformation.cs │ ├── FileHeader.cs │ ├── HwpType │ │ ├── DataRecord.cs │ │ ├── DataTypes.cs │ │ └── TagEnum.cs │ └── SummaryInformation.cs │ ├── HwpReader.cs │ ├── HwpSharp.xproj │ └── project.json └── test ├── HwpSharp.Tests.Console ├── HwpSharp.Tests.Console.xproj ├── Program.cs └── project.json ├── HwpSharp.Tests ├── Hwp5 │ ├── BodyTextTest.cs │ ├── DocumentInformationTest.cs │ ├── DocumentTest.cs │ └── FileHeaderTest.cs ├── HwpSharp.Tests.xproj └── project.json └── case ├── CompoundFile.xls ├── Hwp3File.hwp ├── Hwp5 ├── BlogForm_BookReview.hwp ├── BlogForm_MovieReview.hwp ├── BlogForm_Recipe.hwp ├── BookReview.hwp ├── Hyper(hwp2010).hwp ├── KTX.hwp ├── NewYear_s_Day.hwp ├── Textmail.hwp ├── Worldcup_FIFA2010_32.hwp ├── borderfill.hwp ├── calendar_monthly.hwp ├── calendar_year.hwp ├── classical_literature.hwp ├── distribution.hwp ├── english.hwp ├── interview.hwp ├── multisection.hwp ├── request.hwp ├── shortcut.hwp ├── sungeo.hwp └── treatise sample.hwp └── PdfFile.pdf /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | bower_components/ 163 | 164 | # RIA/Silverlight projects 165 | Generated_Code/ 166 | 167 | # Backup & report files from converting an old project file 168 | # to a newer Visual Studio version. Backup files are not needed, 169 | # because we have git ;-) 170 | _UpgradeReport_Files/ 171 | Backup*/ 172 | UpgradeLog*.XML 173 | UpgradeLog*.htm 174 | 175 | # SQL Server files 176 | *.mdf 177 | *.ldf 178 | 179 | # Business Intelligence projects 180 | *.rdl.data 181 | *.bim.layout 182 | *.bim_*.settings 183 | 184 | # Microsoft Fakes 185 | FakesAssemblies/ 186 | 187 | # LightSwitch generated files 188 | GeneratedArtifacts/ 189 | _Pvt_Extensions/ 190 | ModelManifest.xml 191 | 192 | # ASP.NET 5 generated files 193 | *.lock.json -------------------------------------------------------------------------------- /HwpSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6DA16CB8-1D96-4C11-AC54-5210A16E8AD6}" 7 | EndProject 8 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "HwpSharp", "src\HwpSharp\HwpSharp.xproj", "{E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{99B07E9B-510B-4884-B486-042DC09B333E}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4264F4F8-94A3-4C22-8579-8EB85B7EA473}" 13 | ProjectSection(SolutionItems) = preProject 14 | global.json = global.json 15 | EndProjectSection 16 | EndProject 17 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "HwpSharp.Tests", "test\HwpSharp.Tests\HwpSharp.Tests.xproj", "{07196994-8FEF-417A-9144-05135AA6497A}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "case", "case", "{C1C298A2-EFDC-406B-A9B3-FC671F8EDD76}" 20 | ProjectSection(SolutionItems) = preProject 21 | test\case\CompoundFile.xls = test\case\CompoundFile.xls 22 | test\case\Hwp3File.hwp = test\case\Hwp3File.hwp 23 | test\case\PdfFile.pdf = test\case\PdfFile.pdf 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hwp5", "Hwp5", "{8D356E47-AFA9-4915-B50A-A5AB3573ED0A}" 27 | ProjectSection(SolutionItems) = preProject 28 | test\case\Hwp5\BlogForm_BookReview.hwp = test\case\Hwp5\BlogForm_BookReview.hwp 29 | test\case\Hwp5\BlogForm_MovieReview.hwp = test\case\Hwp5\BlogForm_MovieReview.hwp 30 | test\case\Hwp5\BlogForm_Recipe.hwp = test\case\Hwp5\BlogForm_Recipe.hwp 31 | test\case\Hwp5\BookReview.hwp = test\case\Hwp5\BookReview.hwp 32 | test\case\Hwp5\calendar_monthly.hwp = test\case\Hwp5\calendar_monthly.hwp 33 | test\case\Hwp5\calendar_year.hwp = test\case\Hwp5\calendar_year.hwp 34 | test\case\Hwp5\classical_literature.hwp = test\case\Hwp5\classical_literature.hwp 35 | test\case\Hwp5\english.hwp = test\case\Hwp5\english.hwp 36 | test\case\Hwp5\Hyper(hwp2010).hwp = test\case\Hwp5\Hyper(hwp2010).hwp 37 | test\case\Hwp5\interview.hwp = test\case\Hwp5\interview.hwp 38 | test\case\Hwp5\KTX.hwp = test\case\Hwp5\KTX.hwp 39 | test\case\Hwp5\multisection.hwp = test\case\Hwp5\multisection.hwp 40 | test\case\Hwp5\NewYear_s_Day.hwp = test\case\Hwp5\NewYear_s_Day.hwp 41 | test\case\Hwp5\request.hwp = test\case\Hwp5\request.hwp 42 | test\case\Hwp5\shortcut.hwp = test\case\Hwp5\shortcut.hwp 43 | test\case\Hwp5\sungeo.hwp = test\case\Hwp5\sungeo.hwp 44 | test\case\Hwp5\Textmail.hwp = test\case\Hwp5\Textmail.hwp 45 | test\case\Hwp5\treatise sample.hwp = test\case\Hwp5\treatise sample.hwp 46 | test\case\Hwp5\Worldcup_FIFA2010_32.hwp = test\case\Hwp5\Worldcup_FIFA2010_32.hwp 47 | EndProjectSection 48 | EndProject 49 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "HwpSharp.Tests.Console", "test\HwpSharp.Tests.Console\HwpSharp.Tests.Console.xproj", "{D0E23E50-7881-4442-8E44-73300A7F6D9F}" 50 | EndProject 51 | Global 52 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 53 | Debug|Any CPU = Debug|Any CPU 54 | Release|Any CPU = Release|Any CPU 55 | EndGlobalSection 56 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 57 | {E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {07196994-8FEF-417A-9144-05135AA6497A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {07196994-8FEF-417A-9144-05135AA6497A}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {07196994-8FEF-417A-9144-05135AA6497A}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {07196994-8FEF-417A-9144-05135AA6497A}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {D0E23E50-7881-4442-8E44-73300A7F6D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {D0E23E50-7881-4442-8E44-73300A7F6D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {D0E23E50-7881-4442-8E44-73300A7F6D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {D0E23E50-7881-4442-8E44-73300A7F6D9F}.Release|Any CPU.Build.0 = Release|Any CPU 69 | EndGlobalSection 70 | GlobalSection(SolutionProperties) = preSolution 71 | HideSolutionNode = FALSE 72 | EndGlobalSection 73 | GlobalSection(NestedProjects) = preSolution 74 | {E8AB29C7-8222-4EC8-BFF2-06C1C119ABEA} = {6DA16CB8-1D96-4C11-AC54-5210A16E8AD6} 75 | {07196994-8FEF-417A-9144-05135AA6497A} = {99B07E9B-510B-4884-B486-042DC09B333E} 76 | {C1C298A2-EFDC-406B-A9B3-FC671F8EDD76} = {99B07E9B-510B-4884-B486-042DC09B333E} 77 | {8D356E47-AFA9-4915-B50A-A5AB3573ED0A} = {C1C298A2-EFDC-406B-A9B3-FC671F8EDD76} 78 | {D0E23E50-7881-4442-8E44-73300A7F6D9F} = {99B07E9B-510B-4884-B486-042DC09B333E} 79 | EndGlobalSection 80 | EndGlobal 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yoon SeungYong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hwpSharp 2 | C# Library for hwp document 3 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test" ] 3 | } -------------------------------------------------------------------------------- /src/HwpSharp/Common/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// The exception that is thrown when a file or stream does not contains a HWP document, or HwpSharp cannot recognized a HWP document. 10 | /// 11 | public class HwpFileFormatException : Exception 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public HwpFileFormatException() 17 | { 18 | } 19 | 20 | /// 21 | /// Initializes a new instance of the class with a specified error message. 22 | /// 23 | /// The message that describes the error. 24 | public HwpFileFormatException(string message) 25 | : base(message) 26 | { 27 | } 28 | 29 | /// 30 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 31 | /// 32 | /// The message that describes the error. 33 | /// The exception that is the cause of the current exception. 34 | public HwpFileFormatException(string message, Exception innerException) 35 | : base(message, innerException) 36 | { 37 | } 38 | } 39 | 40 | /// 41 | /// The exception that is thrown when hwpSharp does not support this type of HWP document. 42 | /// 43 | public class HwpUnsupportedFormatException : Exception 44 | { 45 | /// 46 | /// Initializes a new instance of the class. 47 | /// 48 | public HwpUnsupportedFormatException() 49 | { 50 | } 51 | 52 | /// 53 | /// Initializes a new instance of the class with a specified error message. 54 | /// 55 | /// The message that describes the error. 56 | public HwpUnsupportedFormatException(string message) 57 | : base(message) 58 | { 59 | } 60 | 61 | /// 62 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 63 | /// 64 | /// The message that describes the error. 65 | /// The exception that is the cause of the current exception. 66 | public HwpUnsupportedFormatException(string message, Exception innerException) 67 | : base(message, innerException) 68 | { 69 | } 70 | } 71 | 72 | /// 73 | /// The exception that is thrown when hwpSharp read a corrupted file. 74 | /// 75 | public class HwpCorruptedException : Exception 76 | { 77 | /// 78 | /// Initializes a new instance of the class. 79 | /// 80 | public HwpCorruptedException() 81 | { 82 | } 83 | 84 | /// 85 | /// Initializes a new instance of the class with a specified error message. 86 | /// 87 | /// The message that describes the error. 88 | public HwpCorruptedException(string message) 89 | : base(message) 90 | { 91 | } 92 | 93 | /// 94 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 95 | /// 96 | /// The message that describes the error. 97 | /// The exception that is the cause of the current exception. 98 | public HwpCorruptedException(string message, Exception innerException) 99 | : base(message, innerException) 100 | { 101 | } 102 | } 103 | 104 | /// 105 | /// The exception that is thrown when hwpSharp read a corrupted data record from stream. 106 | /// 107 | public class HwpCorruptedDataRecordException : HwpCorruptedException 108 | { 109 | /// 110 | /// Initializes a new instance of the class. 111 | /// 112 | public HwpCorruptedDataRecordException() 113 | { 114 | } 115 | 116 | /// 117 | /// Initializes a new instance of the class with a specified error message. 118 | /// 119 | /// The message that describes the error. 120 | public HwpCorruptedDataRecordException(string message) 121 | : base(message) 122 | { 123 | } 124 | 125 | /// 126 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 127 | /// 128 | /// The message that describes the error. 129 | /// The exception that is the cause of the current exception. 130 | public HwpCorruptedDataRecordException(string message, Exception innerException) 131 | : base(message, innerException) 132 | { 133 | } 134 | } 135 | 136 | /// 137 | /// The exception that is thrown when hwpSharp read a corrupted body text. 138 | /// 139 | public class HwpCorruptedBodyTextException : HwpCorruptedException 140 | { 141 | /// 142 | /// Initializes a new instance of the class. 143 | /// 144 | public HwpCorruptedBodyTextException() 145 | { 146 | } 147 | 148 | /// 149 | /// Initializes a new instance of the class with a specified error message. 150 | /// 151 | /// The message that describes the error. 152 | public HwpCorruptedBodyTextException(string message) 153 | : base(message) 154 | { 155 | } 156 | 157 | /// 158 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. 159 | /// 160 | /// The message that describes the error. 161 | /// The exception that is the cause of the current exception. 162 | public HwpCorruptedBodyTextException(string message, Exception innerException) 163 | : base(message, innerException) 164 | { 165 | } 166 | } 167 | 168 | public class HwpDataRecordConstructorException : Exception 169 | { 170 | } 171 | 172 | public class HwpUnsupportedProperty : Exception 173 | { 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/HwpSharp/Common/IBodyText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// Represents a body text. 10 | /// 11 | public interface IBodyText 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HwpSharp/Common/IDocumentInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// Represents a hwp document information. 10 | /// 11 | public interface IDocumentInformation 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HwpSharp/Common/IFileHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// Represents a file recognition header. 10 | /// 11 | public interface IFileHeader 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HwpSharp/Common/IHwpDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// Represents a Hwp document. 10 | /// 11 | public interface IHwpDocument 12 | { 13 | /// 14 | /// Represents the version of Hwp document. 15 | /// 16 | string HwpVersion { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HwpSharp/Common/ISummaryInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace HwpSharp.Common 7 | { 8 | /// 9 | /// Represents a hwp summary information. 10 | /// 11 | public interface ISummaryInformation 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/BodyText/BodyText.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using HwpSharp.Common; 3 | using OpenMcdf; 4 | 5 | namespace HwpSharp.Hwp5.BodyText 6 | { 7 | /// 8 | /// Represents a hwp 5.0 body text. 9 | /// 10 | public class BodyText : IBodyText 11 | { 12 | public DocumentInformation.DocumentInformation DocumentInformation { get; private set; } 13 | 14 | public List
Sections { get; } 15 | 16 | public BodyText(DocumentInformation.DocumentInformation docInfo) 17 | { 18 | DocumentInformation = docInfo; 19 | Sections = new List
(); 20 | } 21 | 22 | internal BodyText(CFStorage storage, DocumentInformation.DocumentInformation docInfo) 23 | { 24 | DocumentInformation = docInfo; 25 | Sections = new List
(); 26 | 27 | for (var i = 0; i < docInfo.DocumentProperty.SectionCount; ++i) 28 | { 29 | CFStream stream; 30 | try 31 | { 32 | stream = storage.GetStream($"Section{i}"); 33 | } 34 | catch (CFItemNotFound exception) 35 | { 36 | throw new HwpCorruptedBodyTextException("The document does not have some sections. File may be corrupted.", exception); 37 | } 38 | 39 | var section = new Section(stream, docInfo); 40 | 41 | Sections.Add(section); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/BodyText/DataRecords/ParagraphHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using HwpSharp.Hwp5.HwpType; 3 | 4 | namespace HwpSharp.Hwp5.BodyText.DataRecords 5 | { 6 | public class ParagraphHeader : DataRecord 7 | { 8 | public const uint ParagraphHeaderTagId = HwpTagBegin + 50; 9 | 10 | [Flags] 11 | public enum ColumnKind : byte 12 | { 13 | None = 0x00, 14 | Area = 0x01, 15 | MultiColumn = 0x02, 16 | Page = 0x04, 17 | Column = 0x08 18 | } 19 | 20 | public HwpType.UInt32 Length { get; set; } 21 | public HwpType.UInt32 ControlMask { get; set; } 22 | public HwpType.UInt16 ParagraphShapeId { get; set; } 23 | public UInt8 ParagraphStyleId { get; set; } 24 | public ColumnKind ColumnType { get; set; } 25 | public HwpType.UInt16 CharacterShapeCount { get; set; } 26 | public HwpType.UInt16 ParagraphRangeCount { get; set; } 27 | public HwpType.UInt16 LineAlignCount { get; set; } 28 | public HwpType.UInt32 ParagraphId { get; set; } 29 | public HwpType.UInt16 HistoryMergeParagraphFlag { get; set; } 30 | 31 | public ParagraphHeader(uint level, byte[] bytes, 32 | DocumentInformation.DocumentInformation docInfo = null) 33 | : base(ParagraphHeaderTagId, level, (uint) bytes.Length) 34 | { 35 | Length = bytes.ToUInt32(); 36 | if ((Length & 0x80000000u) != 0) 37 | { 38 | Length &= 0x7fffffffu; 39 | } 40 | 41 | ControlMask = bytes.ToUInt32(4); 42 | 43 | ParagraphShapeId = bytes.ToUInt16(8); 44 | 45 | ParagraphStyleId = bytes[10]; 46 | 47 | ColumnKind columnKind; 48 | if (!Enum.TryParse(bytes[11].ToString(), out columnKind)) 49 | { 50 | columnKind = ColumnKind.None; 51 | } 52 | ColumnType = columnKind; 53 | 54 | CharacterShapeCount = bytes.ToUInt16(12); 55 | 56 | ParagraphRangeCount = bytes.ToUInt16(14); 57 | 58 | LineAlignCount = bytes.ToUInt16(16); 59 | 60 | ParagraphId = bytes.ToUInt32(18); 61 | 62 | if (bytes.Length >= 24) 63 | { 64 | HistoryMergeParagraphFlag = bytes.ToUInt16(22); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/BodyText/DataRecords/ParagraphText.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using HwpSharp.Hwp5.HwpType; 3 | 4 | namespace HwpSharp.Hwp5.BodyText.DataRecords 5 | { 6 | public class ParagraphText : DataRecord 7 | { 8 | public const uint ParagraphTextTagId = HwpTagBegin + 51; 9 | public string Text { get; set; } 10 | 11 | public ParagraphText(uint level, byte[] bytes, DocumentInformation.DocumentInformation _ = null) 12 | : base(ParagraphTextTagId, level, (uint) bytes.Length) 13 | { 14 | Text = Encoding.Unicode.GetString(bytes); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/BodyText/Section.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HwpSharp.Hwp5.BodyText.DataRecords; 6 | using HwpSharp.Hwp5.HwpType; 7 | using OpenMcdf; 8 | 9 | namespace HwpSharp.Hwp5.BodyText 10 | { 11 | public class Section 12 | { 13 | public DocumentInformation.DocumentInformation DocumentInformation { get; private set; } 14 | 15 | public List DataRecords { get; } 16 | 17 | public Section(DocumentInformation.DocumentInformation docInfo) 18 | { 19 | DocumentInformation = docInfo; 20 | DataRecords = new List(); 21 | } 22 | 23 | internal Section(CFStream stream, DocumentInformation.DocumentInformation docInfo) 24 | { 25 | DocumentInformation = docInfo; 26 | 27 | var bytes = Document.GetRawBytesFromStream(stream, docInfo.FileHeader); 28 | DataRecords = new List(DataRecord.GetRecordsFromBytes(bytes, docInfo)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/Document.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using HwpSharp.Common; 5 | using HwpSharp.Hwp5.BodyText; 6 | using OpenMcdf; 7 | 8 | namespace HwpSharp.Hwp5 9 | { 10 | /// 11 | /// Represents a hwp 5.0 document. 12 | /// 13 | public class Document : IHwpDocument 14 | { 15 | /// 16 | /// This document is a HWP 5.0 document. 17 | /// 18 | public string HwpVersion => "5.0"; 19 | 20 | /// 21 | /// Gets a file recognition header of this document. 22 | /// 23 | public FileHeader FileHeader { get; private set; } 24 | 25 | /// 26 | /// Gets a document information of this document. 27 | /// 28 | public DocumentInformation.DocumentInformation DocumentInformation { get; private set; } 29 | 30 | /// 31 | /// Gets a body text of this document. 32 | /// 33 | public BodyText.BodyText BodyText { get; private set; } 34 | 35 | /// 36 | /// Gets a summary information of this document. 37 | /// 38 | public SummaryInformation SummaryInformation 39 | { 40 | get { throw new NotImplementedException(); } 41 | private set { throw new NotImplementedException(); } 42 | } 43 | 44 | internal Document(CompoundFile compoundFile) 45 | { 46 | Load(compoundFile); 47 | } 48 | 49 | private void Load(CompoundFile compoundFile) 50 | { 51 | FileHeader = LoadFileHeader(compoundFile); 52 | DocumentInformation = LoadDocumentInformation(compoundFile); 53 | BodyText = LoadBodyText(compoundFile); 54 | } 55 | 56 | private BodyText.BodyText LoadBodyText(CompoundFile compoundFile) 57 | { 58 | CFStorage storage; 59 | try 60 | { 61 | storage = compoundFile.RootStorage.GetStorage("BodyText"); 62 | } 63 | catch (CFItemNotFound exception) 64 | { 65 | throw new HwpFileFormatException("Specified document does not have any BodyText fields.", exception); 66 | } 67 | 68 | var bodyText = new BodyText.BodyText(storage, DocumentInformation); 69 | 70 | return bodyText; 71 | } 72 | 73 | private DocumentInformation.DocumentInformation LoadDocumentInformation(CompoundFile compoundFile) 74 | { 75 | CFStream stream; 76 | try 77 | { 78 | stream = compoundFile.RootStorage.GetStream("DocInfo"); 79 | } 80 | catch (CFItemNotFound exception) 81 | { 82 | throw new HwpFileFormatException("Specified document does not have a DocInfo field.", exception); 83 | } 84 | 85 | var docInfo = new DocumentInformation.DocumentInformation(stream, FileHeader); 86 | 87 | return docInfo; 88 | } 89 | 90 | private static FileHeader LoadFileHeader(CompoundFile compoundFile) 91 | { 92 | CFStream stream; 93 | try 94 | { 95 | stream = compoundFile.RootStorage.GetStream("FileHeader"); 96 | } 97 | catch (CFItemNotFound exception) 98 | { 99 | throw new HwpFileFormatException("Specified document does not have a FileHeader field.", exception); 100 | } 101 | 102 | var fileHeader = new FileHeader(stream); 103 | 104 | return fileHeader; 105 | } 106 | 107 | /// 108 | /// Creates a instance from a . 109 | /// 110 | /// A stream which contains a hwp 5 document. 111 | public Document(Stream stream) 112 | { 113 | if (stream == null) 114 | { 115 | throw new ArgumentNullException(nameof(stream)); 116 | } 117 | 118 | try 119 | { 120 | using (var compoundFile = new CompoundFile(stream, CFSUpdateMode.Update, CFSConfiguration.EraseFreeSectors | CFSConfiguration.SectorRecycle)) 121 | { 122 | Load(compoundFile); 123 | } 124 | } 125 | catch(CFFileFormatException exception) 126 | { 127 | throw new HwpFileFormatException("Specified document is not a hwp 5 document format.", exception); 128 | } 129 | } 130 | 131 | /// 132 | /// Creates a instance from a file. 133 | /// 134 | /// A file name of a hwp 5 document. 135 | public Document(string filename) 136 | { 137 | if (string.IsNullOrWhiteSpace(filename)) 138 | { 139 | throw new ArgumentNullException(nameof(filename)); 140 | } 141 | 142 | try 143 | { 144 | using (var compoundFile = new CompoundFile(filename, CFSUpdateMode.Update, CFSConfiguration.EraseFreeSectors | CFSConfiguration.SectorRecycle)) 145 | { 146 | Load(compoundFile); 147 | } 148 | } 149 | catch (CFFileFormatException exception) 150 | { 151 | throw new HwpFileFormatException("Specified document is not a hwp 5 document format.", exception); 152 | } 153 | } 154 | 155 | internal static byte[] GetRawBytesFromStream(CFStream stream, FileHeader fileHeader) 156 | { 157 | var streamBytes = stream.GetData(); 158 | 159 | if (fileHeader.PasswordEncrypted) 160 | { 161 | throw new HwpUnsupportedFormatException("Does not support a password encrypted document."); 162 | } 163 | 164 | if (fileHeader.Compressed) 165 | { 166 | using (var dataStream = new MemoryStream(streamBytes, false)) 167 | { 168 | using (var zipStream = new DeflateStream(dataStream, CompressionMode.Decompress)) 169 | { 170 | using (var decStream = new MemoryStream()) 171 | { 172 | zipStream.CopyTo(decStream); 173 | 174 | streamBytes = decStream.ToArray(); 175 | } 176 | } 177 | } 178 | } 179 | 180 | return streamBytes; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DataRecords/BinData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using HwpSharp.Common; 5 | using HwpSharp.Hwp5.HwpType; 6 | using UInt16 = HwpSharp.Hwp5.HwpType.UInt16; 7 | 8 | namespace HwpSharp.Hwp5.DocumentInformation.DataRecords 9 | { 10 | public class BinData : DataRecord 11 | { 12 | public const uint BinDataTagId = HwpTagBegin + 2; 13 | 14 | public enum TypeProperty : byte 15 | { 16 | Link = 0, 17 | Enbedding = 1, 18 | Storage = 2, 19 | } 20 | 21 | public enum CompressionProperty : byte 22 | { 23 | StorageDefault = 0, 24 | Compress = 1, 25 | NotCompress = 2, 26 | } 27 | 28 | public enum StateProperty : byte 29 | { 30 | Never = 0, 31 | Success = 1, 32 | Failed = 2, 33 | Ignored = 3, 34 | } 35 | 36 | [DebuggerDisplay("Type={Type}, Compression={Compression}, State={State}")] 37 | public struct BinDataProperty 38 | { 39 | public TypeProperty Type { get; } 40 | 41 | public CompressionProperty Compression { get; } 42 | 43 | public StateProperty State { get; } 44 | 45 | public BinDataProperty(UInt16 value) 46 | { 47 | TypeProperty type; 48 | Type = Enum.TryParse($"{value & 0x7}", out type) ? type : TypeProperty.Link; 49 | 50 | CompressionProperty compression; 51 | Compression = Enum.TryParse($"{(value >> 4) & 0x3}", out compression) 52 | ? compression 53 | : CompressionProperty.StorageDefault; 54 | 55 | StateProperty state; 56 | State = Enum.TryParse($"{(value >> 8) & 0x3}", out state) ? state : StateProperty.Never; 57 | } 58 | 59 | public BinDataProperty(TypeProperty type, CompressionProperty compression, StateProperty state) 60 | { 61 | Type = type; 62 | Compression = compression; 63 | State = state; 64 | } 65 | 66 | public static implicit operator UInt16(BinDataProperty binData) 67 | { 68 | return 69 | (UInt16) 70 | (((ushort) binData.State << 8) + ((ushort) binData.Compression << 4) + (ushort) binData.Type); 71 | } 72 | } 73 | 74 | public BinDataProperty Property { get; } 75 | 76 | private readonly string _linkFileAbsolutePath; 77 | public string LinkFileAbsolutePath 78 | { 79 | get 80 | { 81 | if (Property.Type != TypeProperty.Link) 82 | { 83 | throw new HwpUnsupportedProperty(); 84 | } 85 | return _linkFileAbsolutePath; 86 | } 87 | } 88 | 89 | private readonly string _linkFileRelativePath; 90 | public string LinkFileRelativePath 91 | { 92 | get 93 | { 94 | if (Property.Type != TypeProperty.Link) 95 | { 96 | throw new HwpUnsupportedProperty(); 97 | } 98 | return _linkFileRelativePath; 99 | } 100 | } 101 | 102 | private readonly UInt16 _binaryDataId; 103 | public UInt16 BinaryDataId 104 | { 105 | get 106 | { 107 | if (Property.Type != TypeProperty.Enbedding && Property.Type != TypeProperty.Storage) 108 | { 109 | throw new HwpUnsupportedProperty(); 110 | } 111 | return _binaryDataId; 112 | } 113 | } 114 | 115 | private readonly string _binaryDataExtension; 116 | public string BinaryDataExtension 117 | { 118 | get 119 | { 120 | if (Property.Type != TypeProperty.Enbedding) 121 | { 122 | throw new HwpUnsupportedProperty(); 123 | } 124 | return _binaryDataExtension; 125 | } 126 | } 127 | 128 | public BinData(uint level, byte[] bytes, DocumentInformation _ = null) 129 | : base(BinDataTagId, level, (uint) bytes.Length) 130 | { 131 | var pos = 0; 132 | Property = new BinDataProperty(bytes.ToUInt16()); 133 | pos += 2; 134 | 135 | if (Property.Type == TypeProperty.Link) 136 | { 137 | var absolutePathLenth = bytes.ToWord(pos); 138 | pos += 2; 139 | 140 | _linkFileAbsolutePath = Encoding.Unicode.GetString(bytes, pos, 2*absolutePathLenth); 141 | pos += 2*absolutePathLenth; 142 | 143 | var relativePathLength = bytes.ToWord(pos); 144 | pos += 2; 145 | 146 | _linkFileRelativePath = Encoding.Unicode.GetString(bytes, pos, 2*relativePathLength); 147 | pos += 2*relativePathLength; 148 | } 149 | 150 | if (Property.Type == TypeProperty.Enbedding || Property.Type == TypeProperty.Storage) 151 | { 152 | _binaryDataId = bytes.ToUInt16(pos); 153 | pos += 2; 154 | } 155 | 156 | if (Property.Type == TypeProperty.Enbedding) 157 | { 158 | var extensionLength = bytes.ToWord(pos); 159 | pos += 2; 160 | 161 | _binaryDataExtension = Encoding.Unicode.GetString(bytes, pos, 2*extensionLength); 162 | pos += 2*extensionLength; 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DataRecords/BorderFill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using HwpSharp.Common; 8 | using HwpSharp.Hwp5.HwpType; 9 | using Byte = HwpSharp.Hwp5.HwpType.Byte; 10 | using Int16 = HwpSharp.Hwp5.HwpType.Int16; 11 | using Int32 = HwpSharp.Hwp5.HwpType.Int32; 12 | using UInt16 = HwpSharp.Hwp5.HwpType.UInt16; 13 | 14 | namespace HwpSharp.Hwp5.DocumentInformation.DataRecords 15 | { 16 | public class BorderFill : DataRecord 17 | { 18 | public const uint BorderFillTagId = HwpTagBegin + 4; 19 | 20 | [DebuggerDisplay("Type = {Type}")] 21 | public struct BorderProperty 22 | { 23 | public enum DiagonalType 24 | { 25 | None, 26 | Slash, 27 | BackSlash, 28 | Cross, 29 | Center, 30 | } 31 | 32 | public enum SlashDiagonal : ushort 33 | { 34 | None = 0, 35 | Slash = (1 << 3), 36 | SlashTop = SlashBottom + (1 << 10), 37 | SlashRight = SlashLeft + (1 << 10), 38 | SlashBottom = (1 << 2) + Slash, 39 | SlashLeft = Slash + (1 << 4), 40 | SlashCrooked = Slash + (1 << 8), 41 | } 42 | 43 | public enum BackSlashDiagonal : ushort 44 | { 45 | None = 0, 46 | BackSlash = (1 << 6), 47 | BackSlashTop = BackSlashBottom + (1 << 12), 48 | BackSlashLeft = BackSlashRight + (1 << 12), 49 | BackSlashBottom = (1 << 5) + BackSlash, 50 | BackSlashRight = BackSlash + (1 << 7), 51 | BackSlashCrooked = BackSlash + (1 << 10), 52 | } 53 | 54 | public enum CenterLine : ushort 55 | { 56 | None = 0, 57 | HorizontalCenter = (1 << 8) + (1 << 13), 58 | VerticalCenter = (1 << 9) + (1 << 13), 59 | CrossCenter = (1 << 8) + (1 << 9) + (1 << 13), 60 | } 61 | 62 | public bool CubeEffect { get; } 63 | 64 | public bool ShadeEffect { get; } 65 | 66 | public DiagonalType Type { get; } 67 | 68 | public SlashDiagonal SlashDiagonalType { get; } 69 | 70 | public BackSlashDiagonal BackSlashDiagonalType { get; } 71 | 72 | public CenterLine CenterLineType { get; } 73 | 74 | internal BorderProperty(UInt16 property) 75 | { 76 | CubeEffect = (property & 1) != 0; 77 | 78 | ShadeEffect = (property & 2) != 0; 79 | 80 | SlashDiagonal slash; 81 | SlashDiagonalType = Enum.TryParse($"{property & 0x091C}", out slash) ? slash : SlashDiagonal.None; 82 | 83 | BackSlashDiagonal backSlash; 84 | BackSlashDiagonalType = Enum.TryParse($"{property & 0x14E0}", out backSlash) 85 | ? backSlash 86 | : BackSlashDiagonal.None; 87 | 88 | CenterLine center; 89 | CenterLineType = Enum.TryParse($"{property & 0x2300}", out center) ? center : CenterLine.None; 90 | 91 | if (center != CenterLine.None) 92 | { 93 | Type = DiagonalType.Center; 94 | } 95 | else if (slash != SlashDiagonal.None && backSlash != BackSlashDiagonal.None) 96 | { 97 | Type = DiagonalType.Cross; 98 | } 99 | else if (slash != SlashDiagonal.None) 100 | { 101 | Type = DiagonalType.Slash; 102 | } 103 | else if (backSlash != BackSlashDiagonal.None) 104 | { 105 | Type = DiagonalType.BackSlash; 106 | } 107 | else 108 | { 109 | Type = DiagonalType.None; 110 | } 111 | } 112 | 113 | public BorderProperty(bool cubeEffect = false, bool shadeEffect = false, 114 | DiagonalType type = DiagonalType.None, SlashDiagonal slash = SlashDiagonal.None, 115 | BackSlashDiagonal backSlash = BackSlashDiagonal.None, CenterLine center = CenterLine.None) 116 | { 117 | CubeEffect = cubeEffect; 118 | ShadeEffect = shadeEffect; 119 | Type = type; 120 | SlashDiagonalType = slash; 121 | BackSlashDiagonalType = backSlash; 122 | CenterLineType = center; 123 | } 124 | } 125 | 126 | [SuppressMessage("ReSharper", "InconsistentNaming")] 127 | public enum Weight : byte 128 | { 129 | Mm0_1 = 0, 130 | Mm0_12 = 1, 131 | Mm0_15 = 2, 132 | Mm0_2 = 3, 133 | Mm0_25 = 4, 134 | Mm0_3 = 5, 135 | Mm0_4 = 6, 136 | Mm0_5 = 7, 137 | Mm0_6 = 8, 138 | Mm0_7 = 9, 139 | Mm1_0 = 10, 140 | Mm1_5 = 11, 141 | Mm2_0 = 12, 142 | Mm3_0 = 13, 143 | Mm4_0 = 14, 144 | Mm5_0 = 15, 145 | } 146 | 147 | [DebuggerDisplay("Type = {Type}, Weight = {Weight}, Color = {Color}")] 148 | public struct Border 149 | { 150 | public enum LineType : byte 151 | { 152 | None = 0, 153 | Solid = 1, 154 | Dash = 2, 155 | Dot = 3, 156 | DashDot = 4, 157 | DashDotDot = 5, 158 | LongDash = 6, 159 | Circle = 7, 160 | DoubleLine = 8, 161 | ThinThick = 9, 162 | ThickThin = 10, 163 | ThinThickThin = 11, 164 | Wave = 12, 165 | DoubleWave = 13, 166 | Thick3D = 14, 167 | Thick3DInverse = 15, 168 | Solid3D = 16, 169 | Solid3DInverse = 17, 170 | } 171 | 172 | public LineType Type { get; } 173 | 174 | public Weight Weight { get; } 175 | 176 | public Color Color { get; } 177 | 178 | internal Border(UInt8 type, UInt8 weight, Color color) 179 | { 180 | LineType lineType; 181 | Type = Enum.TryParse($"{type}", out lineType) ? lineType : LineType.Solid; 182 | 183 | Weight weightValue; 184 | Weight = Enum.TryParse($"{weight}", out weightValue) ? weightValue : Weight.Mm0_1; 185 | 186 | Color = color; 187 | } 188 | 189 | public Border(LineType type = LineType.Solid, Weight weight = Weight.Mm0_1, Color color = default(Color)) 190 | { 191 | Type = type; 192 | Weight = weight; 193 | Color = color; 194 | } 195 | } 196 | 197 | [DebuggerDisplay("Type = {Type}, Weight = {Weight}, Color = {Color}")] 198 | public struct Diagonal 199 | { 200 | public enum DiagonalType : byte 201 | { 202 | Slash = 0, 203 | BackSlash = 1, 204 | CrookedSlash = 2, 205 | } 206 | 207 | public DiagonalType Type { get; } 208 | 209 | public Weight Weight { get; } 210 | 211 | public Color Color { get; } 212 | 213 | internal Diagonal(UInt8 type, UInt8 weight, Color color) 214 | { 215 | DiagonalType diagonal; 216 | Type = Enum.TryParse($"{type}", out diagonal) ? diagonal : DiagonalType.Slash; 217 | 218 | Weight weightValue; 219 | Weight = Enum.TryParse($"{weight}", out weightValue) ? weightValue : Weight.Mm0_1; 220 | 221 | Color = color; 222 | } 223 | 224 | public Diagonal(DiagonalType type = DiagonalType.Slash, Weight weight = Weight.Mm0_1, Color color = default(Color)) 225 | { 226 | Type = type; 227 | Weight = weight; 228 | Color = color; 229 | } 230 | } 231 | 232 | public struct Fill 233 | { 234 | [Flags] 235 | public enum FillType 236 | { 237 | None = 0, 238 | Solid = 1, 239 | Image = 2, 240 | Gradation = 4, 241 | } 242 | 243 | [DebuggerDisplay("Background = {Background}, Foreground = {Foreground}, Pattern = {Pattern}")] 244 | public struct SolidFill 245 | { 246 | public enum PatternType 247 | { 248 | None = 0, 249 | Horizontal = 1, 250 | Vertical = 2, 251 | BackSlash = 3, 252 | Slash = 4, 253 | Plus = 5, 254 | Cross = 6, 255 | } 256 | 257 | public Color Background { get; } 258 | 259 | public Color Foreground { get; } 260 | 261 | public PatternType Pattern { get; } 262 | 263 | internal SolidFill(Color background, Color foreground, Int32 pattern) 264 | { 265 | Background = background; 266 | 267 | Foreground = foreground; 268 | 269 | PatternType type; 270 | Pattern = Enum.TryParse($"{pattern}", out type) ? type : PatternType.None; 271 | } 272 | 273 | public SolidFill(Color background = default(Color), Color foreground = default(Color), 274 | PatternType pattern = PatternType.None) 275 | { 276 | Background = background; 277 | Foreground = foreground; 278 | Pattern = pattern; 279 | } 280 | } 281 | 282 | public struct GradationFill 283 | { 284 | public enum GradationType 285 | { 286 | None = 0, 287 | Stripe = 1, 288 | Circular = 2, 289 | Cone = 3, 290 | Square = 4, 291 | } 292 | 293 | public struct GradationColor 294 | { 295 | public Int32? Position { get; } 296 | 297 | public Color Color { get; } 298 | 299 | public GradationColor(Int32? position, Color color) 300 | { 301 | Position = position; 302 | Color = color; 303 | } 304 | } 305 | 306 | public GradationType Type { get; } 307 | 308 | public Int16 Degree { get; } 309 | 310 | public Int16 CenterX { get; } 311 | 312 | public Int16 CenterY { get; } 313 | 314 | public Int16 Blur { get; } 315 | 316 | public IList Colors { get; } 317 | 318 | internal GradationFill(byte[] bytes, int pos = 0) 319 | { 320 | GradationType type; 321 | Type = Enum.TryParse($"{bytes.ToInt16(pos)}", out type) ? type : GradationType.None; 322 | pos += 2; 323 | 324 | Degree = bytes.ToInt16(pos); 325 | pos += 2; 326 | 327 | CenterX = bytes.ToInt16(pos); 328 | pos += 2; 329 | 330 | CenterY = bytes.ToInt16(pos); 331 | pos += 2; 332 | 333 | Blur = bytes.ToInt16(pos); 334 | pos += 2; 335 | 336 | var num = bytes.ToInt16(pos); 337 | pos += 2; 338 | Colors = new List(); 339 | 340 | List positions = null; 341 | if (num > 2) 342 | { 343 | positions = new List(); 344 | for (var i = 0; i < num; i++) 345 | { 346 | positions.Add(bytes.ToInt32(pos)); 347 | pos += 4; 348 | } 349 | } 350 | 351 | for (var i = 0; i < num; i++) 352 | { 353 | Colors.Add(new GradationColor(positions?[i], bytes.ToColor(pos))); 354 | pos += 4; 355 | } 356 | } 357 | 358 | public GradationFill(GradationType type = GradationType.None, Int16 degree = default(Int16), 359 | Int16 centerX = default(Int16), Int16 centerY = default(Int16), Int16 blur = default(Int16), 360 | IEnumerable colors = null) 361 | { 362 | Type = type; 363 | Degree = degree; 364 | CenterX = centerX; 365 | CenterY = centerY; 366 | Blur = blur; 367 | Colors = colors?.ToList() ?? new List(); 368 | } 369 | } 370 | 371 | public struct ImageFill 372 | { 373 | public enum ImageFillType : byte 374 | { 375 | Tile = 0, 376 | TileTop = 1, 377 | TileBottom = 2, 378 | TileLeft = 3, 379 | TileRight = 4, 380 | Fit = 5, 381 | CenterCenter = 6, 382 | CenterTop = 7, 383 | CenterBottom = 8, 384 | LeftCenter = 9, 385 | LeftTop = 10, 386 | LeftBottom = 11, 387 | RightCenter = 12, 388 | RightTop = 13, 389 | RightBottom = 14, 390 | None = 15, 391 | } 392 | 393 | public struct Image 394 | { 395 | [Flags] 396 | public enum ImageEffect 397 | { 398 | None = 0, 399 | Grayscale = 1, 400 | BlackWhite = 2, 401 | Pattern = 4, 402 | } 403 | 404 | public Int8 Brightness { get; } 405 | 406 | public Int8 Contrast { get; } 407 | 408 | public ImageEffect Effect { get; } 409 | 410 | public UInt16 BinItemId { get; } 411 | 412 | internal Image(Int8 brightness, Int8 contrast, Byte effect, UInt16 binItemId) 413 | { 414 | Brightness = brightness; 415 | Contrast = contrast; 416 | 417 | ImageEffect imageEffect; 418 | Effect = Enum.TryParse($"{effect}", out imageEffect) ? imageEffect : ImageEffect.None; 419 | 420 | BinItemId = binItemId; 421 | } 422 | 423 | public Image(Int8 brightness, Int8 contrast, ImageEffect effect, UInt16 binItemId) 424 | { 425 | Brightness = brightness; 426 | Contrast = contrast; 427 | Effect = effect; 428 | BinItemId = binItemId; 429 | } 430 | } 431 | 432 | public ImageFillType Type { get; } 433 | 434 | public Image ImageData { get; } 435 | 436 | public DWord AdditionalGradationSize { get; } 437 | 438 | public Byte GradationCenter { get; } 439 | 440 | public ImageFill(ImageFillType type, Image image, DWord gradationSize, Byte gradationCenter) 441 | { 442 | Type = type; 443 | ImageData = image; 444 | AdditionalGradationSize = gradationSize; 445 | GradationCenter = gradationCenter; 446 | } 447 | } 448 | 449 | public FillType Type { get; } 450 | 451 | private readonly SolidFill _solid; 452 | public SolidFill Solid 453 | { 454 | get 455 | { 456 | if (!Type.HasFlag(FillType.Solid)) 457 | { 458 | throw new HwpUnsupportedProperty(); 459 | } 460 | return _solid; 461 | } 462 | } 463 | 464 | private readonly GradationFill _gradation; 465 | public GradationFill Gradation 466 | { 467 | get 468 | { 469 | if (!Type.HasFlag(FillType.Gradation)) 470 | { 471 | throw new HwpUnsupportedProperty(); 472 | } 473 | return _gradation; 474 | } 475 | } 476 | 477 | private readonly ImageFill _image; 478 | public ImageFill Image 479 | { 480 | get 481 | { 482 | if (!Type.HasFlag(FillType.Image)) 483 | { 484 | throw new HwpUnsupportedProperty(); 485 | } 486 | return _image; 487 | } 488 | } 489 | 490 | public byte[] AdditionalFill { get; } 491 | } 492 | 493 | 494 | public BorderProperty Property { get; } 495 | 496 | public Border Left { get; } 497 | 498 | public Border Right { get; } 499 | 500 | public Border Top { get; } 501 | 502 | public Border Bottom { get; } 503 | 504 | public Diagonal DiagonalProperty { get; } 505 | 506 | public BorderFill(uint level, byte[] bytes, DocumentInformation _ = null) 507 | : base(BorderFillTagId, level, (uint) bytes.Length) 508 | { 509 | var pos = 0; 510 | 511 | Property = new BorderProperty(bytes.ToUInt16()); 512 | pos += 2; 513 | 514 | Left = new Border(bytes[pos], bytes[pos + 1], bytes.ToColor(pos + 2)); 515 | pos += 6; 516 | 517 | Right = new Border(bytes[pos], bytes[pos + 1], bytes.ToColor(pos + 2)); 518 | pos += 6; 519 | 520 | Top = new Border(bytes[pos], bytes[pos + 1], bytes.ToColor(pos + 2)); 521 | pos += 6; 522 | 523 | Bottom = new Border(bytes[pos], bytes[pos + 1], bytes.ToColor(pos + 2)); 524 | pos += 6; 525 | 526 | DiagonalProperty = new Diagonal(bytes[pos], bytes[pos + 1], bytes[pos + 2]); 527 | pos += 6; 528 | } 529 | } 530 | } 531 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DataRecords/DocumentProperty.cs: -------------------------------------------------------------------------------- 1 | using HwpSharp.Hwp5.HwpType; 2 | 3 | namespace HwpSharp.Hwp5.DocumentInformation.DataRecords 4 | { 5 | public class DocumentProperty : DataRecord 6 | { 7 | public const uint DocumentPropertiesTagId = HwpTagBegin; 8 | 9 | public UInt16 SectionCount { get; set; } 10 | public UInt16 StartPageNumber { get; set; } 11 | public UInt16 StartFootNoteNumber { get; set; } 12 | public UInt16 StartEndNoteNumber { get; set; } 13 | public UInt16 StartPictureNumber { get; set; } 14 | public UInt16 StartTableNumber { get; set; } 15 | public UInt16 StartEquationNumber { get; set; } 16 | public UInt32 ListId { get; set; } 17 | public UInt32 ParagraphId { get; set; } 18 | public UInt32 CharacterUnitPosition { get; set; } 19 | 20 | public DocumentProperty(uint level, byte[] bytes, DocumentInformation _ = null) 21 | : base(DocumentPropertiesTagId, level, (uint) bytes.Length) 22 | { 23 | SectionCount = bytes.ToUInt16(); 24 | StartPageNumber = bytes.ToUInt16(2); 25 | StartFootNoteNumber = bytes.ToUInt16(4); 26 | StartEndNoteNumber = bytes.ToUInt16(6); 27 | StartPictureNumber = bytes.ToUInt16(8); 28 | StartTableNumber = bytes.ToUInt16(10); 29 | StartEquationNumber = bytes.ToUInt16(12); 30 | ListId = bytes.ToUInt32(14); 31 | ParagraphId = bytes.ToUInt32(18); 32 | CharacterUnitPosition = bytes.ToUInt32(22); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DataRecords/FaceName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using HwpSharp.Common; 5 | using HwpSharp.Hwp5.HwpType; 6 | using Byte = HwpSharp.Hwp5.HwpType.Byte; 7 | 8 | namespace HwpSharp.Hwp5.DocumentInformation.DataRecords 9 | { 10 | public class FaceName : DataRecord 11 | { 12 | // 파일 형식 문서에 내용이 빠진 부분은 다음을 참조함 13 | // https://github.com/mete0r/pyhwp/tree/develop/pyhwp/hwp5/binmodel/tagid19_face_name.py 14 | 15 | public const uint FaceNameTagId = HwpTagBegin + 3; 16 | 17 | [Flags] 18 | public enum FontProperty : byte 19 | { 20 | Unknown = 0x0, 21 | Ttf = 0x1, 22 | Hft = 0x2, 23 | DefaultFont = 0x20, 24 | FaceStyle = 0x40, 25 | AlternativeFont = 0x80, 26 | } 27 | 28 | public enum AlternativeFont : byte 29 | { 30 | Unknown = 0, 31 | Ttf = 1, 32 | Hft = 2, 33 | } 34 | 35 | public struct FaceStyle 36 | { 37 | public enum FamilyType : byte 38 | { 39 | Any = 0, 40 | None = 1, 41 | TextDisplay = 2, 42 | Script = 3, 43 | Decorative = 4, 44 | Pictorial = 5, 45 | } 46 | 47 | public enum SerifStyle : byte 48 | { 49 | Any = 0, 50 | None = 1, 51 | Cove = 2, 52 | ObtuseCove = 3, 53 | SquareCove = 4, 54 | ObtuseSquareCove = 5, 55 | Square = 6, 56 | Thin = 7, 57 | Bone = 8, 58 | Exaggerated = 9, 59 | Triangle = 10, 60 | NormalSans = 11, 61 | ObtuseSans = 12, 62 | PerpSans = 13, 63 | Flared = 14, 64 | Rounded = 15, 65 | } 66 | 67 | public enum Weight : byte 68 | { 69 | Any = 0, 70 | None = 1, 71 | VeryLight = 2, 72 | Light = 3, 73 | Thin = 4, 74 | Book = 5, 75 | Medium = 6, 76 | Demi = 7, 77 | Bold = 8, 78 | Heavy = 9, 79 | Black = 10, 80 | Nord = 11, 81 | } 82 | 83 | public enum Proportion : byte 84 | { 85 | Any = 0, 86 | None = 1, 87 | OldStyle = 2, 88 | Modern = 3, 89 | EvenWidth = 4, 90 | Expanded = 5, 91 | Condensed = 6, 92 | VeryExpanded = 7, 93 | VeryCondensed = 8, 94 | Monospaced = 9, 95 | } 96 | 97 | public enum Contrast : byte 98 | { 99 | Any = 0, 100 | None = 1, 101 | NoContrast = 2, 102 | VeryLow = 3, 103 | Low = 4, 104 | MediumLow = 5, 105 | Medium = 6, 106 | MediumHigh = 7, 107 | High = 8, 108 | VeryHigh = 9, 109 | } 110 | 111 | public enum StrokeVariation : byte 112 | { 113 | Any = 0, 114 | None = 1, 115 | GradualDiagonal = 2, 116 | GradualTranspositonal = 3, 117 | GradualVertical = 4, 118 | GradualHorizontal = 5, 119 | RapidVertial = 6, 120 | RapidHorizontal = 7, 121 | InstantVertical = 8, 122 | } 123 | 124 | public enum ArmStyle : byte 125 | { 126 | Any = 0, 127 | None = 1, 128 | StraightHorizontal = 2, 129 | StraightWedge = 3, 130 | StraightVertical = 4, 131 | StraightSigleSerif = 5, 132 | StraightDoubleSerif = 6, 133 | BentHorizontal = 7, 134 | BentWedge = 8, 135 | BentVertical = 9, 136 | BentSingleSerif = 10, 137 | BentDoubleSerif = 11, 138 | } 139 | 140 | public enum LetterForm : byte 141 | { 142 | Any = 0, 143 | None = 1, 144 | NormalContact = 2, 145 | NormalWeighted = 3, 146 | NormalBoxed = 4, 147 | NormalFlattened = 5, 148 | NormalRounded = 6, 149 | NormalOffCenter = 7, 150 | NormalSquare = 8, 151 | ObliqueContact = 9, 152 | ObliqueWeighted = 10, 153 | ObliqueBoxed = 11, 154 | ObliqueFlattened = 12, 155 | ObliqueRounded = 13, 156 | ObliqueOffCenter = 14, 157 | ObliqueSquare = 15, 158 | } 159 | 160 | public enum MiddleLine : byte 161 | { 162 | Any = 0, 163 | None = 1, 164 | StandardTrimmed = 2, 165 | StandardPointed = 3, 166 | StandardSerifed = 4, 167 | HighTrimmed = 5, 168 | HighPointed = 6, 169 | HighSerifed = 7, 170 | ConstantTrimmed = 8, 171 | ConstantPointed = 9, 172 | ConstantSerifed = 10, 173 | LowTrimmed = 11, 174 | LowPointed = 12, 175 | LowSerifed = 13, 176 | } 177 | 178 | public enum XHeight : byte 179 | { 180 | Any = 0, 181 | None = 1, 182 | ConstantSmall = 2, 183 | ConstantStandard = 3, 184 | ConstantLarge = 4, 185 | DuckingSmall = 5, 186 | DuckingStandard = 6, 187 | DuckingLarge = 7, 188 | } 189 | 190 | public FamilyType FamilyTypeValue { get; } 191 | 192 | public SerifStyle SerifStyleValue { get; } 193 | 194 | public Weight WeightValue { get; } 195 | 196 | public Proportion ProportionValue { get; } 197 | 198 | public Contrast ContrastValue { get; } 199 | 200 | public StrokeVariation StrokeVariationValue { get; } 201 | 202 | public ArmStyle ArmStyleValue { get; } 203 | 204 | public LetterForm LetterFormValue { get; } 205 | 206 | public MiddleLine MiddleLineValue { get; } 207 | 208 | public XHeight XHeightValue { get; } 209 | 210 | public FaceStyle(FamilyType familyType = FamilyType.Any, SerifStyle serifStyle = SerifStyle.Any, 211 | Weight weight = Weight.Any, Proportion proportion = Proportion.Any, Contrast contrast = Contrast.Any, 212 | StrokeVariation strokeVariation = StrokeVariation.Any, ArmStyle armStyle = ArmStyle.Any, 213 | LetterForm letterForm = LetterForm.Any, MiddleLine middleLine = MiddleLine.Any, 214 | XHeight xHeight = XHeight.Any) 215 | { 216 | FamilyTypeValue = familyType; 217 | SerifStyleValue = serifStyle; 218 | WeightValue = weight; 219 | ProportionValue = proportion; 220 | ContrastValue = contrast; 221 | StrokeVariationValue = strokeVariation; 222 | ArmStyleValue = armStyle; 223 | LetterFormValue = letterForm; 224 | MiddleLineValue = middleLine; 225 | XHeightValue = xHeight; 226 | } 227 | } 228 | 229 | public FontProperty Property { get; } 230 | 231 | public string FontName { get; } 232 | 233 | private readonly AlternativeFont _alternativeFontType; 234 | 235 | public AlternativeFont AlternativeFontType 236 | { 237 | get 238 | { 239 | if (!Property.HasFlag(FontProperty.AlternativeFont)) 240 | { 241 | throw new HwpUnsupportedProperty(); 242 | } 243 | return _alternativeFontType; 244 | } 245 | } 246 | 247 | private readonly string _alternativeFontName; 248 | 249 | public string AlternativeFontName 250 | { 251 | get 252 | { 253 | if (!Property.HasFlag(FontProperty.AlternativeFont)) 254 | { 255 | throw new HwpUnsupportedProperty(); 256 | } 257 | return _alternativeFontName; 258 | } 259 | } 260 | 261 | private readonly FaceStyle _faceStyle; 262 | 263 | public FaceStyle FaceStyleValue 264 | { 265 | get 266 | { 267 | if (!Property.HasFlag(FontProperty.FaceStyle)) 268 | { 269 | throw new HwpUnsupportedProperty(); 270 | } 271 | return _faceStyle; 272 | } 273 | } 274 | 275 | private readonly string _defaultFontName; 276 | 277 | public string DefaultFontName 278 | { 279 | get 280 | { 281 | if (!Property.HasFlag(FontProperty.DefaultFont)) 282 | { 283 | throw new HwpUnsupportedProperty(); 284 | } 285 | return _defaultFontName; 286 | } 287 | } 288 | 289 | public FaceName(uint level, byte[] bytes, DocumentInformation _ = null) 290 | : base(FaceNameTagId, level, (uint) bytes.Length) 291 | { 292 | var pos = 0; 293 | 294 | FontProperty property; 295 | Property = Enum.TryParse($"{bytes[pos]}", out property) ? property : FontProperty.Unknown; 296 | pos += 1; 297 | 298 | var nameLength = bytes.ToWord(pos); 299 | pos += 2; 300 | 301 | FontName = Encoding.Unicode.GetString(bytes, pos, 2*nameLength); 302 | pos += 2*nameLength; 303 | 304 | if (property.HasFlag(FontProperty.AlternativeFont)) 305 | { 306 | AlternativeFont alternative; 307 | _alternativeFontType = Enum.TryParse($"{bytes[pos]}", out alternative) 308 | ? alternative 309 | : AlternativeFont.Unknown; 310 | pos += 1; 311 | 312 | var alternativeLength = bytes.ToWord(pos); 313 | pos += 2; 314 | 315 | _alternativeFontName = Encoding.Unicode.GetString(bytes, pos, 2*alternativeLength); 316 | pos += 2*alternativeLength; 317 | } 318 | 319 | if (property.HasFlag(FontProperty.FaceStyle)) 320 | { 321 | 322 | FaceStyle.FamilyType familyType; 323 | familyType = Enum.TryParse($"{bytes[pos++]}", out familyType) ? familyType : FaceStyle.FamilyType.Any; 324 | 325 | FaceStyle.SerifStyle serifStyle; 326 | serifStyle = Enum.TryParse($"{bytes[pos++]}", out serifStyle) ? serifStyle : FaceStyle.SerifStyle.Any; 327 | 328 | FaceStyle.Weight weight; 329 | weight = Enum.TryParse($"{bytes[pos++]}", out weight) ? weight : FaceStyle.Weight.Any; 330 | 331 | FaceStyle.Proportion proportion; 332 | proportion = Enum.TryParse($"{bytes[pos++]}", out proportion) ? proportion : FaceStyle.Proportion.Any; 333 | 334 | FaceStyle.Contrast contrast; 335 | contrast = Enum.TryParse($"{bytes[pos++]}", out contrast) ? contrast : FaceStyle.Contrast.Any; 336 | 337 | FaceStyle.StrokeVariation strokeVariation; 338 | strokeVariation = Enum.TryParse($"{bytes[pos++]}", out strokeVariation) 339 | ? strokeVariation 340 | : FaceStyle.StrokeVariation.Any; 341 | 342 | FaceStyle.ArmStyle armStyle; 343 | armStyle = Enum.TryParse($"{bytes[pos++]}", out armStyle) ? armStyle : FaceStyle.ArmStyle.Any; 344 | 345 | FaceStyle.LetterForm letterForm; 346 | letterForm = Enum.TryParse($"{bytes[pos++]}", out letterForm) ? letterForm : FaceStyle.LetterForm.Any; 347 | 348 | FaceStyle.MiddleLine middleLine; 349 | middleLine = Enum.TryParse($"{bytes[pos++]}", out middleLine) ? middleLine : FaceStyle.MiddleLine.Any; 350 | 351 | FaceStyle.XHeight xHeight; 352 | xHeight = Enum.TryParse($"{bytes[pos++]}", out xHeight) ? xHeight : FaceStyle.XHeight.Any; 353 | 354 | _faceStyle = new FaceStyle(familyType, serifStyle, weight, proportion, contrast, strokeVariation, 355 | armStyle, letterForm, middleLine, xHeight); 356 | } 357 | 358 | if (property.HasFlag(FontProperty.DefaultFont)) 359 | { 360 | var defaultLength = bytes.ToWord(pos); 361 | pos += 2; 362 | 363 | _defaultFontName = Encoding.Unicode.GetString(bytes, pos, 2*defaultLength); 364 | pos += 2*defaultLength; 365 | } 366 | } 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DataRecords/IdMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using HwpSharp.Hwp5.HwpType; 4 | using Int32 = HwpSharp.Hwp5.HwpType.Int32; 5 | 6 | namespace HwpSharp.Hwp5.DocumentInformation.DataRecords 7 | { 8 | public class IdMapping : DataRecord 9 | { 10 | public const uint IdMappingsTagId = HwpTagBegin + 1; 11 | 12 | public Int32[] IdMappingCounts { get; } 13 | 14 | public Int32 BinaryDataCount => IdMappingCounts[0]; 15 | 16 | public Int32 KoreanFontCount => IdMappingCounts[1]; 17 | 18 | public Int32 EnglishFontCount => IdMappingCounts[2]; 19 | 20 | public Int32 ChineseFontCount => IdMappingCounts[3]; 21 | 22 | public Int32 JapaneseFontCount => IdMappingCounts[4]; 23 | 24 | public Int32 OtherFontCount => IdMappingCounts[5]; 25 | 26 | public Int32 SymbolFontCount => IdMappingCounts[6]; 27 | 28 | public Int32 UserFontCount => IdMappingCounts[7]; 29 | 30 | public Int32 BorderBackgroundCount => IdMappingCounts[8]; 31 | 32 | public Int32 CharacterShapeCount => IdMappingCounts[9]; 33 | 34 | public Int32 TabDefinitionCount => IdMappingCounts[10]; 35 | 36 | public Int32 ParagraphNumberCount => IdMappingCounts[11]; 37 | 38 | public Int32 ListHeaderTableCount => IdMappingCounts[12]; 39 | 40 | public Int32 ParagraphShapeCount => IdMappingCounts[13]; 41 | 42 | public Int32 StyleCount => IdMappingCounts[14]; 43 | 44 | public Int32 MemoShapeCount 45 | { 46 | get 47 | { 48 | if (IdMappingCounts.Length < 15) 49 | { 50 | throw new NotSupportedException(); 51 | } 52 | 53 | return IdMappingCounts[15]; 54 | } 55 | } 56 | 57 | public Int32 TrackChangeCount 58 | { 59 | get 60 | { 61 | if (IdMappingCounts.Length < 16) 62 | { 63 | throw new NotSupportedException(); 64 | } 65 | 66 | return IdMappingCounts[16]; 67 | } 68 | } 69 | 70 | public Int32 TrackChangeUserCount 71 | { 72 | get 73 | { 74 | if (IdMappingCounts.Length < 17) 75 | { 76 | throw new NotSupportedException(); 77 | } 78 | 79 | return IdMappingCounts[17]; 80 | } 81 | } 82 | 83 | public IdMapping(uint level, byte[] bytes, DocumentInformation _ = null) 84 | : base(IdMappingsTagId, level, (uint) bytes.Length) 85 | { 86 | var mappings = new List(); 87 | for (var pos = 0; pos < bytes.Length; pos += 4) 88 | { 89 | mappings.Add(bytes.ToInt32(pos)); 90 | } 91 | IdMappingCounts = mappings.ToArray(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/DocumentInformation/DocumentInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using HwpSharp.Common; 5 | using HwpSharp.Hwp5.DocumentInformation.DataRecords; 6 | using HwpSharp.Hwp5.HwpType; 7 | using OpenMcdf; 8 | 9 | namespace HwpSharp.Hwp5.DocumentInformation 10 | { 11 | /// 12 | /// Represents a hwp 5.0 document information. 13 | /// 14 | public class DocumentInformation : IDocumentInformation 15 | { 16 | public IList DataRecords { get; } 17 | 18 | public FileHeader FileHeader { get; } 19 | 20 | public DocumentProperty DocumentProperty { get; } 21 | 22 | public IdMapping IdMappings { get; } 23 | 24 | /// 25 | /// Creates a blank instance with specified file header. 26 | /// 27 | /// Hwp 5 file header 28 | public DocumentInformation(FileHeader fileHeader) 29 | { 30 | FileHeader = fileHeader; 31 | } 32 | 33 | internal DocumentInformation(CFStream stream, FileHeader fileHeader) 34 | { 35 | FileHeader = fileHeader; 36 | 37 | var bytes = Document.GetRawBytesFromStream(stream, fileHeader); 38 | 39 | var records = DataRecord.GetRecordsFromBytes(bytes); 40 | 41 | try 42 | { 43 | DataRecords = records as IList ?? records.ToList(); 44 | DocumentProperty = (DocumentProperty) DataRecords.Single(r => r.TagId == DocumentProperty.DocumentPropertiesTagId); 45 | IdMappings = (IdMapping) DataRecords.Single(r => r.TagId == IdMapping.IdMappingsTagId); 46 | } 47 | catch (InvalidOperationException exception) 48 | { 49 | throw new HwpFileFormatException( 50 | "This document does not contains a document property data record or contains more than one document property data record.", 51 | exception); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/FileHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using HwpSharp.Common; 4 | using OpenMcdf; 5 | 6 | namespace HwpSharp.Hwp5 7 | { 8 | /// 9 | /// Represents a hwp 5.0 file header. 10 | /// 11 | public class FileHeader : IFileHeader 12 | { 13 | private const int SignatureLength = 32; 14 | private static readonly byte[] SignatureBytes = 15 | { 16 | 0x48, 0x57, 0x50, 0x20, 0x44, 0x6f, 0x63, 0x75, 17 | 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x69, 0x6c, 18 | 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 20 | }; // "HWP Document File" 21 | 22 | private const int VersionLength = 4; 23 | private const int AttributeLength = 4; 24 | 25 | /// 26 | /// Gets or sets the document file version. 27 | /// 28 | public Version FileVersion { get; set; } 29 | 30 | /// 31 | /// Gets or sets the compressed attribute. 32 | /// 33 | public bool Compressed { get; set; } 34 | 35 | /// 36 | /// Gets or sets the encryption attribute. 37 | /// 38 | public bool PasswordEncrypted { get; set; } 39 | 40 | /// 41 | /// Gets or sets the published document attribute. 42 | /// 43 | public bool Published { get; set; } 44 | 45 | /// 46 | /// Gets or sets whether the document has scripts. 47 | /// 48 | public bool HasScript { get; set; } 49 | 50 | /// 51 | /// Gets or sets the DRM secure attribute. 52 | /// 53 | public bool DrmSecured { get; set; } 54 | 55 | /// 56 | /// Gets or sets whether the document has XML template storages. 57 | /// 58 | public bool HasXmlTemplateStorage { get; set; } 59 | 60 | /// 61 | /// Gets or sets whether the document history is managed. 62 | /// 63 | public bool HasHistory { get; set; } 64 | 65 | /// 66 | /// Gets or sets whether the document has a digital sign. 67 | /// 68 | public bool HasSign { get; set; } 69 | 70 | /// 71 | /// Gets or sets the certificate encryption attribute. 72 | /// 73 | public bool CertificateEncrypted { get; set; } 74 | 75 | /// 76 | /// Gets or sets whether the additional certificate is stored. 77 | /// 78 | public bool CertificateReserved { get; set; } 79 | 80 | /// 81 | /// Gets or sets whether the document is encrypted with certificate and DRM. 82 | /// 83 | public bool CertificateDrmSecured { get; set; } 84 | 85 | /// 86 | /// Gets or sets the CCL document attribute. 87 | /// 88 | public bool CclDocumented { get; set; } 89 | 90 | /// 91 | /// Creates a instance with a blank setting. 92 | /// 93 | public FileHeader() 94 | { 95 | FileVersion = new Version(5, 0, 0, 0); 96 | } 97 | 98 | internal FileHeader(CFStream stream) 99 | { 100 | SetFileHeader(stream); 101 | } 102 | 103 | internal void SetFileHeader(CFStream stream) 104 | { 105 | ParseSignature(stream); 106 | ParseFileVersion(stream); 107 | ParseAttribute(stream); 108 | } 109 | 110 | private void ParseAttribute(CFStream stream) 111 | { 112 | var attributes = new byte[AttributeLength]; 113 | var readCount = stream.Read(attributes, 36, AttributeLength); 114 | 115 | if (readCount != AttributeLength) 116 | { 117 | throw new HwpFileFormatException("File attribute field is corrupted. File may be corrupted."); 118 | } 119 | 120 | Compressed = (attributes[0] & 0x1u) != 0; 121 | PasswordEncrypted = (attributes[0] & 0x2u) != 0; 122 | Published = (attributes[0] & 0x4u) != 0; 123 | HasScript = (attributes[0] & 0x8u) != 0; 124 | DrmSecured = (attributes[0] & 0x10u) != 0; 125 | HasXmlTemplateStorage = (attributes[0] & 0x20u) != 0; 126 | HasHistory = (attributes[0] & 0x40u) != 0; 127 | HasSign = (attributes[0] & 0x80u) != 0; 128 | CertificateEncrypted = (attributes[1] & 0x1u) != 0; 129 | CertificateReserved = (attributes[1] & 0x2u) != 0; 130 | CertificateDrmSecured = (attributes[1] & 0x4u) != 0; 131 | CclDocumented = (attributes[1] & 0x8u) != 0; 132 | } 133 | 134 | private void ParseFileVersion(CFStream stream) 135 | { 136 | var versionBytes = new byte[VersionLength]; 137 | var readCount = stream.Read(versionBytes, 32, VersionLength); 138 | 139 | if (readCount != VersionLength) 140 | { 141 | throw new HwpFileFormatException("File does not have a version field. File may be corrupted."); 142 | } 143 | 144 | FileVersion = new Version(versionBytes[3], versionBytes[2], versionBytes[1], versionBytes[0]); 145 | 146 | if (FileVersion.Major != 5 || FileVersion.Minor != 0) 147 | { 148 | throw new HwpFileFormatException($"File version '{FileVersion}' is not imcompatible."); 149 | } 150 | } 151 | 152 | private static void ParseSignature(CFStream stream) 153 | { 154 | var signature = new byte[SignatureLength]; 155 | var readCount = stream.Read(signature, 0, SignatureLength); 156 | 157 | if (readCount != SignatureLength || !SignatureBytes.SequenceEqual(signature)) 158 | { 159 | throw new HwpFileFormatException("Signature is not matched. File may be corrupted."); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/HwpType/DataRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using HwpSharp.Common; 5 | using HwpSharp.Hwp5.BodyText.DataRecords; 6 | using HwpSharp.Hwp5.DocumentInformation.DataRecords; 7 | 8 | namespace HwpSharp.Hwp5.HwpType 9 | { 10 | /// 11 | /// Represents a data record of hwp 5 document. 12 | /// 13 | public abstract class DataRecord 14 | { 15 | internal const int HwpTagBegin = 0x10; 16 | 17 | /// 18 | /// Gets the tag id of data record. 19 | /// 20 | public uint TagId { get; internal set; } 21 | 22 | /// 23 | /// Gets the level of data record. 24 | /// 25 | public uint Level { get; internal set; } 26 | 27 | /// 28 | /// Gets the size of data record. 29 | /// 30 | public DWord Size { get; internal set; } 31 | 32 | protected DataRecord(uint tagId, uint level, DWord size) 33 | { 34 | TagId = tagId; 35 | Level = level; 36 | Size = size; 37 | } 38 | 39 | internal static IEnumerable GetRecordsFromBytes(byte[] bytes, DocumentInformation.DocumentInformation docInfo = null) 40 | { 41 | var records = new List(); 42 | 43 | var pos = 0; 44 | while (pos < bytes.Length) 45 | { 46 | var header = bytes.ToDWord(pos); 47 | pos += 4; 48 | 49 | var tagId = header & 0x3FF; 50 | var level = (header >> 10) & 0x3FF; 51 | var size = header >> 20; 52 | if (size == 0xfff) 53 | { 54 | size = bytes.ToDWord(pos); 55 | pos += 4; 56 | } 57 | 58 | var recordBytes = bytes.Skip(pos).Take((int) size).ToArray(); 59 | pos += (int) size; 60 | var record = DataRecordFactory.Create(tagId, level, recordBytes, docInfo); 61 | 62 | if (record != null) 63 | { 64 | records.Add(record); 65 | } 66 | } 67 | 68 | return records; 69 | } 70 | } 71 | 72 | public static class DataRecordFactory 73 | { 74 | private static readonly Dictionary DataRecordTypes = new Dictionary 75 | { 76 | {DocumentProperty.DocumentPropertiesTagId, typeof (DocumentProperty)}, 77 | {IdMapping.IdMappingsTagId, typeof (IdMapping)}, 78 | {BinData.BinDataTagId, typeof (BinData)}, 79 | {FaceName.FaceNameTagId, typeof (FaceName)}, 80 | {BorderFill.BorderFillTagId, typeof (BorderFill)}, 81 | 82 | {ParagraphHeader.ParagraphHeaderTagId, typeof (ParagraphHeader)}, 83 | {ParagraphText.ParagraphTextTagId, typeof (ParagraphText)}, 84 | }; 85 | 86 | public static void RegisterType(uint tagId, Type type) 87 | { 88 | DataRecordTypes[tagId] = type; 89 | } 90 | 91 | public static DataRecord Create(uint tagId, uint level, byte[] data, DocumentInformation.DocumentInformation docInfo = null) 92 | { 93 | if (!DataRecordTypes.ContainsKey(tagId)) 94 | { 95 | return new DataRecordImpl(tagId, level, data); 96 | } 97 | 98 | var ctor = 99 | DataRecordTypes[tagId].GetConstructor(new[] 100 | {typeof (uint), typeof (byte[]), typeof (DocumentInformation.DocumentInformation)}); 101 | if (ctor == null) 102 | { 103 | throw new HwpDataRecordConstructorException(); 104 | } 105 | return ctor.Invoke(new object[] {level, data, docInfo}) as DataRecord; 106 | } 107 | } 108 | 109 | internal class DataRecordImpl : DataRecord 110 | { 111 | public byte[] Data { get; private set; } 112 | 113 | public DataRecordImpl(uint tagId, uint level, byte[] data = null) 114 | : base(tagId, level, (uint) (data?.Length ?? 0)) 115 | { 116 | Data = data; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/HwpType/DataTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace HwpSharp.Hwp5.HwpType 7 | { 8 | /// 9 | /// Unsigned 1 byte (0-255). 10 | /// 11 | [DebuggerDisplay("{_value}")] 12 | [DebuggerTypeProxy(typeof(byte))] 13 | public struct Byte 14 | { 15 | private readonly byte _value; 16 | 17 | private Byte(byte value) 18 | { 19 | _value = value; 20 | } 21 | 22 | /// 23 | /// Implicitly converts a to a . 24 | /// 25 | /// The to convert. 26 | /// A new with the specified value. 27 | public static implicit operator Byte(byte value) 28 | { 29 | return new Byte(value); 30 | } 31 | 32 | /// 33 | /// Implicitly converts a to a . 34 | /// 35 | /// The to convert. 36 | /// A that is the specified 's value. 37 | public static implicit operator byte(Byte value) 38 | { 39 | return value._value; 40 | } 41 | 42 | public override string ToString() 43 | { 44 | return _value.ToString(); 45 | } 46 | } 47 | 48 | /// 49 | /// 'unsigned int' on the 16bit compiler. 50 | /// 51 | [DebuggerDisplay("{_value}")] 52 | [DebuggerTypeProxy(typeof(ushort))] 53 | public struct Word 54 | { 55 | private readonly ushort _value; 56 | 57 | private Word(ushort value) 58 | { 59 | _value = value; 60 | } 61 | 62 | /// 63 | /// Implicitly converts a to a . 64 | /// 65 | /// The to convert. 66 | /// A new with the specified value. 67 | public static implicit operator Word(ushort value) 68 | { 69 | return new Word(value); 70 | } 71 | 72 | /// 73 | /// Implicitly converts a to a . 74 | /// 75 | /// The to convert. 76 | /// A that is the specified 's value. 77 | public static implicit operator ushort(Word value) 78 | { 79 | return value._value; 80 | } 81 | 82 | public override string ToString() 83 | { 84 | return _value.ToString(); 85 | } 86 | } 87 | 88 | /// 89 | /// 'unsigned long' on the 16bit compiler. 90 | /// 91 | [DebuggerDisplay("{_value}")] 92 | [DebuggerTypeProxy(typeof(uint))] 93 | public struct DWord 94 | { 95 | private readonly uint _value; 96 | 97 | private DWord(uint value) 98 | { 99 | _value = value; 100 | } 101 | 102 | /// 103 | /// Implicitly converts a to a . 104 | /// 105 | /// The to convert. 106 | /// A new with the specified value. 107 | public static implicit operator DWord(uint value) 108 | { 109 | return new DWord(value); 110 | } 111 | 112 | /// 113 | /// Implicitly converts a to a . 114 | /// 115 | /// The to convert. 116 | /// A that is the specified 's value. 117 | public static implicit operator uint(DWord value) 118 | { 119 | return value._value; 120 | } 121 | 122 | public override string ToString() 123 | { 124 | return _value.ToString(); 125 | } 126 | } 127 | 128 | /// 129 | /// Unicode based character, default code in HWP. 130 | /// 131 | [DebuggerDisplay("{_value}")] 132 | [DebuggerTypeProxy(typeof(char))] 133 | public struct WChar 134 | { 135 | private readonly char _value; 136 | 137 | private WChar(char value) 138 | { 139 | _value = value; 140 | } 141 | 142 | /// 143 | /// Implicitly converts a to a . 144 | /// 145 | /// The to convert. 146 | /// A new with the specified value. 147 | public static implicit operator WChar(char value) 148 | { 149 | return new WChar(value); 150 | } 151 | 152 | /// 153 | /// Implicitly converts a to a . 154 | /// 155 | /// The to convert. 156 | /// A that is the specified 's value. 157 | public static implicit operator char(WChar value) 158 | { 159 | return value._value; 160 | } 161 | 162 | public override string ToString() 163 | { 164 | return _value.ToString(); 165 | } 166 | } 167 | 168 | /// 169 | /// HWP internal unit, represented as 1/1700 inch. 170 | /// 171 | [DebuggerDisplay("{_value}")] 172 | [DebuggerTypeProxy(typeof(uint))] 173 | public struct HwpUnit 174 | { 175 | private readonly uint _value; 176 | 177 | private HwpUnit(uint value) 178 | { 179 | _value = value; 180 | } 181 | 182 | /// 183 | /// Implicitly converts a to a . 184 | /// 185 | /// The to convert. 186 | /// A new with the specified value. 187 | public static implicit operator HwpUnit(uint value) 188 | { 189 | return new HwpUnit(value); 190 | } 191 | 192 | /// 193 | /// Implicitly converts a to a . 194 | /// 195 | /// The to convert. 196 | /// A that is the specified 's value. 197 | public static implicit operator uint(HwpUnit value) 198 | { 199 | return value._value; 200 | } 201 | 202 | public override string ToString() 203 | { 204 | return _value.ToString(); 205 | } 206 | } 207 | 208 | /// 209 | /// HWP internal unit, represented as 1/1700 inch with sign. 210 | /// 211 | [DebuggerDisplay("{_value}")] 212 | [DebuggerTypeProxy(typeof(int))] 213 | public struct SHwpUnit 214 | { 215 | private readonly int _value; 216 | 217 | private SHwpUnit(int value) 218 | { 219 | _value = value; 220 | } 221 | 222 | /// 223 | /// Implicitly converts a to a . 224 | /// 225 | /// The to convert. 226 | /// A new with the specified value. 227 | public static implicit operator SHwpUnit(int value) 228 | { 229 | return new SHwpUnit(value); 230 | } 231 | 232 | /// 233 | /// Implicitly converts a to a . 234 | /// 235 | /// The to convert. 236 | /// A that is the specified 's value. 237 | public static implicit operator int(SHwpUnit value) 238 | { 239 | return value._value; 240 | } 241 | 242 | public override string ToString() 243 | { 244 | return _value.ToString(); 245 | } 246 | } 247 | 248 | /// 249 | /// 'unsigned __int8' 250 | /// 251 | [DebuggerDisplay("{_value}")] 252 | [DebuggerTypeProxy(typeof(byte))] 253 | public struct UInt8 254 | { 255 | private readonly byte _value; 256 | 257 | private UInt8(byte value) 258 | { 259 | _value = value; 260 | } 261 | 262 | /// 263 | /// Implicitly converts a to a . 264 | /// 265 | /// The to convert. 266 | /// A new with the specified value. 267 | public static implicit operator UInt8(byte value) 268 | { 269 | return new UInt8(value); 270 | } 271 | 272 | /// 273 | /// Implicitly converts a to a . 274 | /// 275 | /// The to convert. 276 | /// A that is the specified 's value. 277 | public static implicit operator byte(UInt8 value) 278 | { 279 | return value._value; 280 | } 281 | 282 | public override string ToString() 283 | { 284 | return _value.ToString(); 285 | } 286 | } 287 | 288 | /// 289 | /// 'unsigned __int16' 290 | /// 291 | [DebuggerDisplay("{_value}")] 292 | [DebuggerTypeProxy(typeof(ushort))] 293 | public struct UInt16 294 | { 295 | private readonly ushort _value; 296 | 297 | private UInt16(ushort value) 298 | { 299 | _value = value; 300 | } 301 | 302 | /// 303 | /// Implicitly converts a to a . 304 | /// 305 | /// The to convert. 306 | /// A new with the specified value. 307 | public static implicit operator UInt16(ushort value) 308 | { 309 | return new UInt16(value); 310 | } 311 | 312 | /// 313 | /// Implicitly converts a to a . 314 | /// 315 | /// The to convert. 316 | /// A that is the specified 's value. 317 | public static implicit operator ushort(UInt16 value) 318 | { 319 | return value._value; 320 | } 321 | 322 | public override string ToString() 323 | { 324 | return _value.ToString(); 325 | } 326 | } 327 | 328 | /// 329 | /// 'unsigned __int32' 330 | /// 331 | [DebuggerDisplay("{_value}")] 332 | [DebuggerTypeProxy(typeof(uint))] 333 | public struct UInt32 334 | { 335 | private readonly uint _value; 336 | 337 | private UInt32(uint value) 338 | { 339 | _value = value; 340 | } 341 | 342 | /// 343 | /// Implicitly converts a to a . 344 | /// 345 | /// The to convert. 346 | /// A new with the specified value. 347 | public static implicit operator UInt32(uint value) 348 | { 349 | return new UInt32(value); 350 | } 351 | 352 | /// 353 | /// Implicitly converts a to a . 354 | /// 355 | /// The to convert. 356 | /// A that is the specified 's value. 357 | public static implicit operator uint(UInt32 value) 358 | { 359 | return value._value; 360 | } 361 | 362 | public override string ToString() 363 | { 364 | return _value.ToString(); 365 | } 366 | } 367 | 368 | /// 369 | /// 'signed __int8' 370 | /// 371 | [DebuggerDisplay("{_value}")] 372 | [DebuggerTypeProxy(typeof(sbyte))] 373 | public struct Int8 374 | { 375 | private readonly sbyte _value; 376 | 377 | private Int8(sbyte value) 378 | { 379 | _value = value; 380 | } 381 | 382 | /// 383 | /// Implicitly converts a to a . 384 | /// 385 | /// The to convert. 386 | /// A new with the specified value. 387 | public static implicit operator Int8(sbyte value) 388 | { 389 | return new Int8(value); 390 | } 391 | 392 | /// 393 | /// Implicitly converts a to a . 394 | /// 395 | /// The to convert. 396 | /// A that is the specified 's value. 397 | public static implicit operator sbyte(Int8 value) 398 | { 399 | return value._value; 400 | } 401 | 402 | public override string ToString() 403 | { 404 | return _value.ToString(); 405 | } 406 | } 407 | 408 | /// 409 | /// 'signed __int16' 410 | /// 411 | [DebuggerDisplay("{_value}")] 412 | [DebuggerTypeProxy(typeof(short))] 413 | public struct Int16 414 | { 415 | private readonly short _value; 416 | 417 | private Int16(short value) 418 | { 419 | _value = value; 420 | } 421 | 422 | /// 423 | /// Implicitly converts a to a . 424 | /// 425 | /// The to convert. 426 | /// A new with the specified value. 427 | public static implicit operator Int16(short value) 428 | { 429 | return new Int16(value); 430 | } 431 | 432 | /// 433 | /// Implicitly converts a to a . 434 | /// 435 | /// The to convert. 436 | /// A that is the specified 's value. 437 | public static implicit operator short(Int16 value) 438 | { 439 | return value._value; 440 | } 441 | 442 | public override string ToString() 443 | { 444 | return _value.ToString(); 445 | } 446 | } 447 | 448 | /// 449 | /// 'signed __int32' 450 | /// 451 | [DebuggerDisplay("{_value}")] 452 | [DebuggerTypeProxy(typeof(int))] 453 | public struct Int32 454 | { 455 | private readonly int _value; 456 | 457 | private Int32(int value) 458 | { 459 | _value = value; 460 | } 461 | 462 | /// 463 | /// Implicitly converts a to a . 464 | /// 465 | /// The to convert. 466 | /// A new with the specified value. 467 | public static implicit operator Int32(int value) 468 | { 469 | return new Int32(value); 470 | } 471 | 472 | /// 473 | /// Implicitly converts a to a . 474 | /// 475 | /// The to convert. 476 | /// A that is the specified 's value. 477 | public static implicit operator int(Int32 value) 478 | { 479 | return value._value; 480 | } 481 | 482 | public override string ToString() 483 | { 484 | return _value.ToString(); 485 | } 486 | } 487 | 488 | /// 489 | /// Same as INT16 490 | /// 491 | [DebuggerDisplay("{_value}")] 492 | [DebuggerTypeProxy(typeof(short))] 493 | public struct HwpUnit16 494 | { 495 | private readonly short _value; 496 | 497 | private HwpUnit16(short value) 498 | { 499 | _value = value; 500 | } 501 | 502 | /// 503 | /// Implicitly converts a to a . 504 | /// 505 | /// The to convert. 506 | /// A new with the specified value. 507 | public static implicit operator HwpUnit16(short value) 508 | { 509 | return new HwpUnit16(value); 510 | } 511 | 512 | /// 513 | /// Implicitly converts a to a . 514 | /// 515 | /// The to convert. 516 | /// A that is the specified 's value. 517 | public static implicit operator short(HwpUnit16 value) 518 | { 519 | return value._value; 520 | } 521 | 522 | public override string ToString() 523 | { 524 | return _value.ToString(); 525 | } 526 | } 527 | 528 | /// 529 | /// Represents RGB value(0x00bbggrr) as decimal. 530 | /// 531 | [DebuggerDisplay("({Red}, {Green}, {Blue})")] 532 | public struct Color 533 | { 534 | public byte Red { get; } 535 | public byte Green { get; } 536 | public byte Blue { get; } 537 | 538 | private Color(uint value) 539 | { 540 | Red = (byte) (value & 0xff); 541 | Green = (byte) ((value >> 8) & 0xff); 542 | Blue = (byte) ((value >> 16) & 0xff); 543 | } 544 | 545 | public Color(byte r, byte g, byte b) 546 | { 547 | Red = r; 548 | Green = g; 549 | Blue = b; 550 | } 551 | 552 | /// 553 | /// Implicitly converts a to a . 554 | /// 555 | /// The to convert. 556 | /// A new with the specified value. 557 | public static implicit operator Color(uint value) 558 | { 559 | return new Color(value); 560 | } 561 | 562 | /// 563 | /// Implicitly converts a to a . 564 | /// 565 | /// The to convert. 566 | /// A that is the specified 's value. 567 | public static implicit operator uint(Color value) 568 | { 569 | return value.Red + value.Green*0x100u + value.Blue*0x10000u; 570 | } 571 | 572 | public override string ToString() 573 | { 574 | return $"({Red}, {Green}, {Blue})"; 575 | } 576 | } 577 | 578 | public static class ByteArrayConverter 579 | { 580 | public static Word ToWord(this IEnumerable bytes, int offset = 0) 581 | { 582 | var arr = bytes.Skip(offset).Take(2).ToArray(); 583 | if (arr.Length < 2) 584 | { 585 | throw new ArgumentOutOfRangeException(nameof(bytes), "Word needs at least 2 bytes."); 586 | } 587 | 588 | return (ushort) (arr[0] + arr[1]*0x100u); 589 | } 590 | 591 | public static DWord ToDWord(this IEnumerable bytes, int offset = 0) 592 | { 593 | var arr = bytes.Skip(offset).Take(4).ToArray(); 594 | if (arr.Length < 4) 595 | { 596 | throw new ArgumentOutOfRangeException(nameof(bytes), "DWord needs at least 4 bytes."); 597 | } 598 | 599 | return arr[0] + arr[1]*0x100u + arr[2]*0x10000u + arr[3]*0x1000000u; 600 | } 601 | 602 | public static WChar ToWChar(this IEnumerable bytes, int offset = 0) 603 | { 604 | var arr = bytes.Skip(offset).Take(2).ToArray(); 605 | if (arr.Length < 2) 606 | { 607 | throw new ArgumentOutOfRangeException(nameof(bytes), "WChar needs at least 2 bytes."); 608 | } 609 | 610 | return (char) (arr[0] + arr[1]*0x100u); 611 | } 612 | 613 | public static HwpUnit ToHwpUnit(this IEnumerable bytes, int offset = 0) 614 | { 615 | var arr = bytes.Skip(offset).Take(4).ToArray(); 616 | if (arr.Length < 4) 617 | { 618 | throw new ArgumentOutOfRangeException(nameof(bytes), "HwpUnit needs at least 4 bytes."); 619 | } 620 | 621 | return arr[0] + arr[1]*0x100u + arr[2]*0x10000u + arr[3]*0x1000000u; 622 | } 623 | 624 | public static SHwpUnit ToSHwpUnit(this IEnumerable bytes, int offset = 0) 625 | { 626 | var arr = bytes.Skip(offset).Take(4).ToArray(); 627 | if (arr.Length < 4) 628 | { 629 | throw new ArgumentOutOfRangeException(nameof(bytes), "SHwpUnit needs at least 4 bytes."); 630 | } 631 | 632 | return arr[0] + arr[1]*0x100 + arr[2]*0x10000 + arr[3]*0x1000000; 633 | } 634 | 635 | public static UInt16 ToUInt16(this IEnumerable bytes, int offset = 0) 636 | { 637 | var arr = bytes.Skip(offset).Take(2).ToArray(); 638 | if (arr.Length < 2) 639 | { 640 | throw new ArgumentOutOfRangeException(nameof(bytes), "UInt16 needs at least 2 bytes."); 641 | } 642 | 643 | return (ushort) (arr[0] + arr[1]*0x100u); 644 | } 645 | 646 | public static UInt32 ToUInt32(this IEnumerable bytes, int offset = 0) 647 | { 648 | var arr = bytes.Skip(offset).Take(4).ToArray(); 649 | if (arr.Length < 4) 650 | { 651 | throw new ArgumentOutOfRangeException(nameof(bytes), "UInt32 needs at least 4 bytes."); 652 | } 653 | 654 | return arr[0] + arr[1]*0x100u + arr[2]*0x10000u + arr[3]*0x1000000u; 655 | } 656 | 657 | public static Int16 ToInt16(this IEnumerable bytes, int offset = 0) 658 | { 659 | var arr = bytes.Skip(offset).Take(2).ToArray(); 660 | if (arr.Length < 2) 661 | { 662 | throw new ArgumentOutOfRangeException(nameof(bytes), "Int16 needs at least 2 bytes."); 663 | } 664 | 665 | return (short) (arr[0] + arr[1]*0x100); 666 | } 667 | 668 | public static Int32 ToInt32(this IEnumerable bytes, int offset = 0) 669 | { 670 | var arr = bytes.Skip(offset).Take(4).ToArray(); 671 | if (arr.Length < 4) 672 | { 673 | throw new ArgumentOutOfRangeException(nameof(bytes), "Int32 needs at least 4 bytes."); 674 | } 675 | 676 | return arr[0] + arr[1]*0x100 + arr[2]*0x10000 + arr[3]*0x1000000; 677 | } 678 | 679 | public static HwpUnit16 ToHwpUnit16(this IEnumerable bytes, int offset = 0) 680 | { 681 | var arr = bytes.Skip(offset).Take(2).ToArray(); 682 | if (arr.Length < 2) 683 | { 684 | throw new ArgumentOutOfRangeException(nameof(bytes), "HwpUnit16 needs at least 2 bytes."); 685 | } 686 | 687 | return (short) (arr[0] + arr[1]*0x100); 688 | } 689 | 690 | public static Color ToColor(this IEnumerable bytes, int offset = 0) 691 | { 692 | var arr = bytes.Skip(offset).Take(4).ToArray(); 693 | if (arr.Length < 4) 694 | { 695 | throw new ArgumentOutOfRangeException(nameof(bytes), "Color needs at least 4 bytes."); 696 | } 697 | 698 | return new Color(arr[0], arr[1], arr[2]); 699 | } 700 | } 701 | } -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/HwpType/TagEnum.cs: -------------------------------------------------------------------------------- 1 | namespace HwpSharp.Hwp5.HwpType 2 | { 3 | /// 4 | /// Specifies a tag ID of a hwp 5 data record. 5 | /// 6 | public enum TagEnum 7 | { 8 | Unknown = -1, 9 | 10 | #region DocInfo 11 | 12 | /// 13 | /// Document property. 14 | /// 15 | DocumentProperties = DataRecord.HwpTagBegin, 16 | IdMappings = DataRecord.HwpTagBegin + 1, 17 | BinDate = DataRecord.HwpTagBegin + 2, 18 | FaceName = DataRecord.HwpTagBegin + 3, 19 | BorderFill = DataRecord.HwpTagBegin + 4, 20 | CharShape = DataRecord.HwpTagBegin + 5, 21 | TabDef = DataRecord.HwpTagBegin + 6, 22 | Numbering = DataRecord.HwpTagBegin + 7, 23 | Bullet = DataRecord.HwpTagBegin + 8, 24 | ParaShape = DataRecord.HwpTagBegin + 9, 25 | Style = DataRecord.HwpTagBegin + 10, 26 | DocData = DataRecord.HwpTagBegin + 11, 27 | DistributeDocData = DataRecord.HwpTagBegin + 12, 28 | CompatibleDocument = DataRecord.HwpTagBegin + 14, 29 | LayoutCompatibility = DataRecord.HwpTagBegin + 15, 30 | TrackChange = DataRecord.HwpTagBegin + 16, 31 | MemoShape = DataRecord.HwpTagBegin + 76, 32 | ForbiddenChar = DataRecord.HwpTagBegin + 78, 33 | TrackChangeShape = DataRecord.HwpTagBegin + 80, 34 | TrackChangeAuthor = DataRecord.HwpTagBegin + 81, 35 | #endregion 36 | 37 | #region BodyText 38 | ParagraphHeader = DataRecord.HwpTagBegin + 50, 39 | ParagraphText = DataRecord.HwpTagBegin + 51, 40 | ParagraphCharacterShape = DataRecord.HwpTagBegin + 52, 41 | ParagraphLineSegment = DataRecord.HwpTagBegin + 53, 42 | ParagraphRangeTag = DataRecord.HwpTagBegin + 54, 43 | ControlHeader = DataRecord.HwpTagBegin + 55, 44 | ListHeader = DataRecord.HwpTagBegin + 56, 45 | PageDefinition = DataRecord.HwpTagBegin + 57, 46 | FootnoteShape = DataRecord.HwpTagBegin + 58, 47 | PageBorderFill = DataRecord.HwpTagBegin + 59, 48 | ShapeComponent = DataRecord.HwpTagBegin + 60, 49 | Table = DataRecord.HwpTagBegin + 61, 50 | ShapeComponentLine = DataRecord.HwpTagBegin + 62, 51 | ShapeComponentRectangle = DataRecord.HwpTagBegin + 63, 52 | ShapeComponentEllipse = DataRecord.HwpTagBegin + 64, 53 | ShapeComponentArc = DataRecord.HwpTagBegin + 65, 54 | ShapeComponentPolygon = DataRecord.HwpTagBegin + 66, 55 | ShapeComponentCurve = DataRecord.HwpTagBegin + 67, 56 | ShapeComponentOle = DataRecord.HwpTagBegin + 68, 57 | ShapeComponentPicture = DataRecord.HwpTagBegin + 69, 58 | ShapeComponentContainer = DataRecord.HwpTagBegin + 70, 59 | ControlData = DataRecord.HwpTagBegin + 71, 60 | Equation = DataRecord.HwpTagBegin + 72, 61 | ShapeComponentTextArt = DataRecord.HwpTagBegin + 74, 62 | FormObject = DataRecord.HwpTagBegin + 75, 63 | MemoList = DataRecord.HwpTagBegin + 77, 64 | ChartData = DataRecord.HwpTagBegin + 79, 65 | VideoData = DataRecord.HwpTagBegin + 82, 66 | ShapeComponentUnknown = DataRecord.HwpTagBegin + 99 67 | #endregion 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/HwpSharp/Hwp5/SummaryInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HwpSharp.Common; 6 | 7 | namespace HwpSharp.Hwp5 8 | { 9 | /// 10 | /// Represents a hwp 5.0 summary information. 11 | /// 12 | public class SummaryInformation : ISummaryInformation 13 | { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/HwpSharp/HwpReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using HwpSharp.Common; 4 | using HwpSharp.Hwp5; 5 | 6 | namespace HwpSharp 7 | { 8 | /// 9 | /// Represents a Hwp document reader. 10 | /// 11 | public static class HwpReader 12 | { 13 | private static class Signature 14 | { 15 | public static readonly byte[] Hwp3 = 16 | { 17 | 0x48, 0x57, 0x50, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x69, 0x6c, 18 | 0x65, 0x20, 0x56, 0x33, 0x2e, 0x30, 0x30, 0x20, 0x1a, 0x01, 0x02, 0x03, 0x04, 0x05 19 | }; 20 | 21 | public static readonly byte[] CompoundFile = 22 | { 23 | 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 24 | }; 25 | } 26 | 27 | private static class FileType 28 | { 29 | public const string Hwp3 = @"HWP 3.0"; 30 | public const string CompoundFile = @"Compound File"; 31 | } 32 | 33 | /// 34 | /// Returns a from a hwp document file. 35 | /// 36 | /// Path to a hwp document. 37 | /// instance. 38 | public static IHwpDocument Read(string filename) 39 | { 40 | using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) 41 | { 42 | return Read(stream); 43 | } 44 | } 45 | 46 | /// 47 | /// Returns a from a stream. 48 | /// 49 | /// Stream of a hwp document. 50 | /// instance. 51 | public static IHwpDocument Read(Stream stream) 52 | { 53 | var fileType = GetFileType(stream); 54 | if (fileType == FileType.CompoundFile) 55 | { 56 | return new Document(stream); 57 | } 58 | throw new HwpFileFormatException("File type is imcompatible."); 59 | } 60 | 61 | private static string GetFileType(Stream stream) 62 | { 63 | var memoryStream = ReadFromStream(stream); 64 | 65 | if (IsHwp30Format(memoryStream)) 66 | { 67 | return FileType.Hwp3; 68 | } 69 | 70 | if (IsCompoundFileFormat(memoryStream)) 71 | { 72 | return FileType.CompoundFile; 73 | } 74 | 75 | throw new HwpFileFormatException("File type is unknown."); 76 | } 77 | 78 | private static MemoryStream ReadFromStream(Stream stream) 79 | { 80 | try 81 | { 82 | var memoryStream = new MemoryStream(); 83 | while (true) 84 | { 85 | var buf = new byte[8192]; 86 | var read = stream.Read(buf, 0, buf.Length); 87 | if (read > 0) 88 | { 89 | memoryStream.Write(buf, 0, read); 90 | } 91 | else 92 | { 93 | break; 94 | } 95 | } 96 | memoryStream.Position = 0; 97 | return memoryStream; 98 | } 99 | finally 100 | { 101 | stream.Close(); 102 | } 103 | } 104 | 105 | private static bool IsHwp30Format(Stream stream) 106 | { 107 | var read = 0; 108 | try 109 | { 110 | var firstBytes = new byte[30]; 111 | read = stream.Read(firstBytes, 0, 30); 112 | return Signature.Hwp3.SequenceEqual(firstBytes); 113 | } 114 | finally 115 | { 116 | if (read > 0) 117 | { 118 | stream.Seek(-read, SeekOrigin.Current); 119 | } 120 | } 121 | } 122 | 123 | private static bool IsCompoundFileFormat(Stream stream) 124 | { 125 | var read = 0; 126 | try 127 | { 128 | var firstBytes = new byte[8]; 129 | read = stream.Read(firstBytes, 0, 8); 130 | 131 | return Signature.CompoundFile.SequenceEqual(firstBytes); 132 | } 133 | finally 134 | { 135 | if (read > 0) 136 | { 137 | stream.Seek(-read, SeekOrigin.Current); 138 | } 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/HwpSharp/HwpSharp.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | e8ab29c7-8222-4ec8-bff2-06c1c119abea 10 | HwpSharp 11 | ..\artifacts\obj\$(MSBuildProjectName) 12 | .\bin\ 13 | 14 | 15 | HwpSharp 16 | 17 | 18 | 2.0 19 | 20 | 21 | True 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/HwpSharp/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ "Yoon SeungYong" ], 3 | "dependencies": { 4 | "OpenMcdf": "2.0.5739.40493" 5 | }, 6 | "description": "C# Library for HWP documents", 7 | "frameworks": { 8 | "net45": { 9 | "frameworkAssemblies": { 10 | "System": "4.0.0.0", 11 | "System.Runtime": "4.0.0.0", 12 | "System.Collections": "4.0.0.0", 13 | "System.Linq": "4.0.0.0" 14 | } 15 | } 16 | }, 17 | "packOptions": { 18 | "licenseUrl": "https://raw.githubusercontent.com/forcom/HwpSharp/master/LICENSE", 19 | "projectUrl": "https://github.com/forcom/HwpSharp", 20 | "tags": [ "" ] 21 | }, 22 | "version": "1.0.0-*" 23 | } 24 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests.Console/HwpSharp.Tests.Console.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | d0e23e50-7881-4442-8e44-73300a7f6d9f 10 | HwpSharp.Tests.Console 11 | ..\..\artifacts\obj\$(MSBuildProjectName) 12 | .\bin\ 13 | 14 | 15 | 2.0 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests.Console/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using HwpSharp.Hwp5; 7 | using HwpSharp.Hwp5.BodyText.DataRecords; 8 | 9 | namespace HwpSharp.Tests.Console 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var doc = new Document(@"..\case\Hwp5\distribution.hwp"); 16 | foreach (var section in doc.BodyText.Sections) 17 | { 18 | if (section == null) 19 | continue; 20 | foreach (var record in section.DataRecords) 21 | { 22 | if (record?.TagId != ParagraphText.ParagraphTextTagId) 23 | continue; 24 | System.Console.WriteLine(((ParagraphText) record)?.Text); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests.Console/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0-*", 3 | "description": "HwpSharp.Tests.Console Console Application", 4 | "authors": [ "Y" ], 5 | "tags": [ "" ], 6 | "projectUrl": "", 7 | "licenseUrl": "", 8 | 9 | "dependencies": { 10 | "HwpSharp": "1.0.0-*" 11 | }, 12 | 13 | "commands": { 14 | "HwpSharp.Tests.Console": "HwpSharp.Tests.Console" 15 | }, 16 | 17 | "buildOptions": { 18 | "emitEntryPoint": true 19 | }, 20 | "frameworks": { 21 | "net451": { } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/Hwp5/BodyTextTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using HwpSharp.Hwp5; 6 | using HwpSharp.Hwp5.BodyText.DataRecords; 7 | using HwpSharp.Hwp5.HwpType; 8 | using Xunit; 9 | 10 | namespace HwpSharp.Tests.Hwp5 11 | { 12 | public class BodyTextTest 13 | { 14 | [Theory] 15 | // [InlineData(@"../../../../../case/Hwp5/BlogForm_BookReview.hwp", null)] 16 | // [InlineData(@"../../../../../case/Hwp5/BlogForm_MovieReview.hwp", null)] 17 | // [InlineData(@"../../../../../case/Hwp5/BlogForm_Recipe.hwp", null)] 18 | // [InlineData(@"../../../../../case/Hwp5/BookReview.hwp", null)] 19 | // [InlineData(@"../../../../../case/Hwp5/calendar_monthly.hwp", null)] 20 | // [InlineData(@"../../../../../case/Hwp5/calendar_year.hwp", null)] 21 | // [InlineData(@"../../../../../case/Hwp5/classical_literature.hwp", null)] 22 | // [InlineData(@"../../../../../case/Hwp5/english.hwp", null)] 23 | // [InlineData(@"../../../../../case/Hwp5/Hyper(hwp2010).hwp", null)] 24 | // [InlineData(@"../../../../../case/Hwp5/interview.hwp", null)] 25 | // [InlineData(@"../../../../../case/Hwp5/KTX.hwp", null)] 26 | // [InlineData(@"../../../../../case/Hwp5/NewYear_s_Day.hwp", null)] 27 | // [InlineData(@"../../../../../case/Hwp5/request.hwp", null)] 28 | // [InlineData(@"../../../../../case/Hwp5/shortcut.hwp", null)] 29 | // [InlineData(@"../../../../../case/Hwp5/sungeo.hwp", null)] 30 | // [InlineData(@"../../../../../case/Hwp5/Textmail.hwp", null)] 31 | // [InlineData(@"../../../../../case/Hwp5/treatise sample.hwp", null)] 32 | // [InlineData(@"../../../../../case/Hwp5/Worldcup_FIFA2010_32.hwp", null)] 33 | [InlineData(@"../../../../../case/Hwp5/multisection.hwp", "\u0002\u6364\u7365\0\0\0\0\u0002\u0002\u6c64\u636f\0\0\0\0\u0002\u0015\u6e70\u7067\0\0\0\0\u0015MultiSection 01\r")] 34 | public void ParagraphText_NormalHwp5Document(string filename, string expectedBodyText) 35 | { 36 | var document = new Document(filename); 37 | 38 | var paragraph = 39 | document.BodyText.Sections[0].DataRecords.Where( 40 | record => record.TagId == ParagraphText.ParagraphTextTagId) 41 | .Cast() 42 | .Aggregate("", (x, y) => x + y?.Text); 43 | Assert.Equal(expectedBodyText, paragraph); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/Hwp5/DocumentInformationTest.cs: -------------------------------------------------------------------------------- 1 | using HwpSharp.Hwp5; 2 | using Xunit; 3 | 4 | namespace HwpSharp.Tests.Hwp5 5 | { 6 | public class DocumentInformationTest 7 | { 8 | [Theory] 9 | [InlineData(@"../../../../../case/Hwp5/BlogForm_BookReview.hwp", 1, 1)] 10 | [InlineData(@"../../../../../case/Hwp5/BlogForm_MovieReview.hwp", 1, 1)] 11 | [InlineData(@"../../../../../case/Hwp5/BlogForm_Recipe.hwp", 1, 1)] 12 | [InlineData(@"../../../../../case/Hwp5/BookReview.hwp", 2, 1)] 13 | [InlineData(@"../../../../../case/Hwp5/calendar_monthly.hwp", 1, 1)] 14 | [InlineData(@"../../../../../case/Hwp5/calendar_year.hwp", 1, 1)] 15 | [InlineData(@"../../../../../case/Hwp5/classical_literature.hwp", 1, 1)] 16 | [InlineData(@"../../../../../case/Hwp5/english.hwp", 1, 1)] 17 | [InlineData(@"../../../../../case/Hwp5/Hyper(hwp2010).hwp", 1, 1)] 18 | [InlineData(@"../../../../../case/Hwp5/interview.hwp", 2, 1)] 19 | [InlineData(@"../../../../../case/Hwp5/KTX.hwp", 1, 1)] 20 | [InlineData(@"../../../../../case/Hwp5/NewYear_s_Day.hwp", 1, 1)] 21 | [InlineData(@"../../../../../case/Hwp5/request.hwp", 1, 1)] 22 | [InlineData(@"../../../../../case/Hwp5/shortcut.hwp", 1, 1)] 23 | [InlineData(@"../../../../../case/Hwp5/sungeo.hwp", 13, 1)] 24 | [InlineData(@"../../../../../case/Hwp5/Textmail.hwp", 1, 1)] 25 | [InlineData(@"../../../../../case/Hwp5/treatise sample.hwp", 1, 1)] 26 | [InlineData(@"../../../../../case/Hwp5/Worldcup_FIFA2010_32.hwp", 1, 1)] 27 | [InlineData(@"../../../../../case/Hwp5/multisection.hwp", 3, 1)] 28 | public void DocumentProperty_NormalHwp5Document(string filename, ushort expectedSectionCount, ushort expectedStartPageNumber) 29 | { 30 | var document = new Document(filename); 31 | 32 | Assert.Equal(expectedSectionCount, (ushort) document.DocumentInformation.DocumentProperty.SectionCount); 33 | Assert.Equal(expectedStartPageNumber, (ushort) document.DocumentInformation.DocumentProperty.StartPageNumber); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/Hwp5/DocumentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using HwpSharp.Common; 4 | using HwpSharp.Hwp5; 5 | using Xunit; 6 | 7 | namespace HwpSharp.Tests.Hwp5 8 | { 9 | public class DocumentTest 10 | { 11 | [Theory] 12 | [InlineData(@"../../../../../case/CompoundFile.xls", typeof(HwpFileFormatException), "Specified document does not have a FileHeader field.")] 13 | [InlineData(@"../../../../../case/Hwp3File.hwp", typeof(HwpFileFormatException), "Specified document is not a hwp 5 document format.")] 14 | [InlineData(@"../../../../../case/PdfFile.pdf", typeof(HwpFileFormatException), "Specified document is not a hwp 5 document format.")] 15 | [InlineData(null, typeof(ArgumentNullException), "Value cannot be null.")] 16 | [InlineData(@"Non-Exist Document", typeof(FileNotFoundException), "Could not find file")] 17 | public void ConstructorWithFileName_AbnormalHwp5Document(string filename, Type expectedException, string expectedMessage) 18 | { 19 | var ex = Record.Exception(() => 20 | { 21 | new Document(filename); 22 | }); 23 | 24 | Assert.IsType(expectedException, ex); 25 | Assert.StartsWith(expectedMessage, ex.Message); 26 | } 27 | 28 | [Theory] 29 | [InlineData(@"../../../../../case/CompoundFile.xls", typeof(HwpFileFormatException), "Specified document does not have a FileHeader field.")] 30 | [InlineData(@"../../../../../case/Hwp3File.hwp", typeof(HwpFileFormatException), "Specified document is not a hwp 5 document format.")] 31 | [InlineData(@"../../../../../case/PdfFile.pdf", typeof(HwpFileFormatException), "Specified document is not a hwp 5 document format.")] 32 | [InlineData(null, typeof(ArgumentNullException), "Value cannot be null.")] 33 | public void ConstructorWithStream_AbnormalHwp5Document(string filename, Type expectedException, string expectedMessage) 34 | { 35 | var ex = Record.Exception(() => 36 | { 37 | var stream = filename != null ? new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) : null; 38 | new Document(stream); 39 | }); 40 | 41 | Assert.IsType(expectedException, ex); 42 | Assert.StartsWith(expectedMessage, ex.Message); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/Hwp5/FileHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Threading.Tasks; 7 | using HwpSharp.Common; 8 | using HwpSharp.Hwp5; 9 | using Xunit; 10 | 11 | namespace HwpSharp.Tests.Hwp5 12 | { 13 | public class FileHeaderTest 14 | { 15 | [Theory] 16 | [InlineData(@"../../../../../case/Hwp5/BlogForm_BookReview.hwp", "5.0.3.0")] 17 | [InlineData(@"../../../../../case/Hwp5/BlogForm_MovieReview.hwp", "5.0.3.0")] 18 | [InlineData(@"../../../../../case/Hwp5/BlogForm_Recipe.hwp", "5.0.3.0")] 19 | [InlineData(@"../../../../../case/Hwp5/BookReview.hwp", "5.0.3.0")] 20 | [InlineData(@"../../../../../case/Hwp5/calendar_monthly.hwp", "5.0.3.0")] 21 | [InlineData(@"../../../../../case/Hwp5/calendar_year.hwp", "5.0.3.0")] 22 | [InlineData(@"../../../../../case/Hwp5/classical_literature.hwp", "5.0.3.0")] 23 | [InlineData(@"../../../../../case/Hwp5/english.hwp", "5.0.3.2")] 24 | [InlineData(@"../../../../../case/Hwp5/Hyper(hwp2010).hwp", "5.0.3.3")] 25 | [InlineData(@"../../../../../case/Hwp5/interview.hwp", "5.0.3.0")] 26 | [InlineData(@"../../../../../case/Hwp5/KTX.hwp", "5.0.3.0")] 27 | [InlineData(@"../../../../../case/Hwp5/NewYear_s_Day.hwp", "5.0.3.2")] 28 | [InlineData(@"../../../../../case/Hwp5/request.hwp", "5.0.3.2")] 29 | [InlineData(@"../../../../../case/Hwp5/shortcut.hwp", "5.0.3.0")] 30 | [InlineData(@"../../../../../case/Hwp5/sungeo.hwp", "5.0.3.0")] 31 | [InlineData(@"../../../../../case/Hwp5/Textmail.hwp", "5.0.3.0")] 32 | [InlineData(@"../../../../../case/Hwp5/treatise sample.hwp", "5.0.3.0")] 33 | [InlineData(@"../../../../../case/Hwp5/Worldcup_FIFA2010_32.hwp", "5.0.3.0")] 34 | public void Version_NormalHwp5Document(string filename, string expected) 35 | { 36 | var document = new Document(filename); 37 | 38 | Assert.Equal(expected, document.FileHeader.FileVersion.ToString()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/HwpSharp.Tests.xproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 14.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 07196994-8fef-417a-9144-05135aa6497a 10 | HwpSharp.Tests 11 | ..\..\artifacts\obj\$(MSBuildProjectName) 12 | .\bin\ 13 | 14 | 15 | HwpSharp.Tests 16 | 17 | 18 | 2.0 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/HwpSharp.Tests/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ "Yoon SeungYong" ], 3 | "testRunner": "xunit", 4 | "dependencies": { 5 | "HwpSharp": "1.0.0-*", 6 | "xunit": "2.2.0-beta2-build3300", 7 | "dotnet-test-xunit": "2.2.0-preview2-build1029" 8 | }, 9 | "description": "", 10 | "frameworks": { 11 | "net451": { 12 | "dependencies": { 13 | "Microsoft.NETCore.Platforms": "1.0.1" 14 | } 15 | } 16 | }, 17 | "version": "1.0.0-*" 18 | } -------------------------------------------------------------------------------- /test/case/CompoundFile.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/CompoundFile.xls -------------------------------------------------------------------------------- /test/case/Hwp3File.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp3File.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/BlogForm_BookReview.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/BlogForm_BookReview.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/BlogForm_MovieReview.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/BlogForm_MovieReview.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/BlogForm_Recipe.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/BlogForm_Recipe.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/BookReview.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/BookReview.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/Hyper(hwp2010).hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/Hyper(hwp2010).hwp -------------------------------------------------------------------------------- /test/case/Hwp5/KTX.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/KTX.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/NewYear_s_Day.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/NewYear_s_Day.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/Textmail.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/Textmail.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/Worldcup_FIFA2010_32.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/Worldcup_FIFA2010_32.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/borderfill.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/borderfill.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/calendar_monthly.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/calendar_monthly.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/calendar_year.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/calendar_year.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/classical_literature.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/classical_literature.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/distribution.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/distribution.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/english.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/english.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/interview.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/interview.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/multisection.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/multisection.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/request.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/request.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/shortcut.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/shortcut.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/sungeo.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/sungeo.hwp -------------------------------------------------------------------------------- /test/case/Hwp5/treatise sample.hwp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/Hwp5/treatise sample.hwp -------------------------------------------------------------------------------- /test/case/PdfFile.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forcom/HwpSharp/c3b617ec53e0a9344c183d19b0dd351ee0fca215/test/case/PdfFile.pdf --------------------------------------------------------------------------------