├── RolerFileToolkit
├── Samples
│ ├── FileToolkitSample.UWP
│ │ ├── Assets
│ │ │ ├── StoreLogo.png
│ │ │ ├── SplashScreen.scale-200.png
│ │ │ ├── LockScreenLogo.scale-200.png
│ │ │ ├── Square150x150Logo.scale-200.png
│ │ │ ├── Square44x44Logo.scale-200.png
│ │ │ ├── Wide310x150Logo.scale-200.png
│ │ │ └── Square44x44Logo.targetsize-24_altform-unplated.png
│ │ ├── App.xaml
│ │ ├── Properties
│ │ │ ├── AssemblyInfo.cs
│ │ │ └── Default.rd.xml
│ │ ├── MainPage.xaml
│ │ ├── Package.appxmanifest
│ │ ├── MainPage.xaml.cs
│ │ ├── App.xaml.cs
│ │ └── FileToolkitSample.UWP.csproj
│ └── FileToolkitSample.WPF
│ │ ├── App.config
│ │ ├── Properties
│ │ ├── Settings.settings
│ │ ├── Settings.Designer.cs
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resx
│ │ ├── App.xaml.cs
│ │ ├── App.xaml
│ │ ├── MainWindow.xaml
│ │ ├── MainWindow.xaml.cs
│ │ └── FileToolkitSample.WPF.csproj
├── Roler.Toolkit.File.Epub
│ ├── Entity
│ │ ├── Ncx
│ │ │ ├── NavInfo.cs
│ │ │ ├── NavPointLabel.cs
│ │ │ ├── NavPointContent.cs
│ │ │ ├── Ncx.cs
│ │ │ ├── NavMap.cs
│ │ │ └── NavPoint.cs
│ │ ├── Nav
│ │ │ ├── NavSpan.cs
│ │ │ ├── NavHead.cs
│ │ │ ├── Nav.cs
│ │ │ ├── NavA.cs
│ │ │ ├── NavOl.cs
│ │ │ ├── NavLi.cs
│ │ │ └── NavElement.cs
│ │ ├── Container.cs
│ │ └── Opf
│ │ │ ├── Manifest.cs
│ │ │ ├── SpineItemRef.cs
│ │ │ ├── LinkElement.cs
│ │ │ ├── DcElement.cs
│ │ │ ├── Spine.cs
│ │ │ ├── ManifestItem.cs
│ │ │ ├── Package.cs
│ │ │ ├── MetaElement.cs
│ │ │ └── Metadata.cs
│ ├── Chapter.cs
│ ├── ContentFile.cs
│ ├── Define
│ │ └── Const.cs
│ ├── Helper
│ │ └── PathHelper.cs
│ ├── Structure.cs
│ ├── Roler.Toolkit.File.Epub.csproj
│ ├── ExtendMethod.cs
│ ├── Epub.cs
│ ├── Engine
│ │ ├── ContainerEngine.cs
│ │ ├── NcxEngine.cs
│ │ ├── NavEngine.cs
│ │ └── OpfEngine.cs
│ └── EpubReader.cs
├── Roler.Toolkit.File.Mobi
│ ├── Entity
│ │ ├── IndxHeader
│ │ │ ├── IndexType.cs
│ │ │ └── IndxHeader.cs
│ │ ├── MobiHeader
│ │ │ ├── TextEncoding.cs
│ │ │ ├── MobiType.cs
│ │ │ └── MobiHeader.cs
│ │ ├── AudiRecord.cs
│ │ ├── VideRecord.cs
│ │ ├── PalmDOCHeader
│ │ │ ├── EncryptionType.cs
│ │ │ ├── CompressionType.cs
│ │ │ └── PalmDOCHeader.cs
│ │ ├── PalmDB
│ │ │ ├── PalmDBRecordAttribute.cs
│ │ │ ├── PalmDBRecordInfo.cs
│ │ │ ├── PalmDBAttribute.cs
│ │ │ └── PalmDB.cs
│ │ ├── CmetRecord.cs
│ │ ├── ExthHeader
│ │ │ ├── ExthHeader.cs
│ │ │ ├── ExthRecord.cs
│ │ │ └── ExthRecordType.cs
│ │ ├── SrcsRecord.cs
│ │ ├── FlisRecord.cs
│ │ └── FcisRecord.cs
│ ├── Compression
│ │ ├── ICompression.cs
│ │ ├── NoneCompression.cs
│ │ ├── BitReader.cs
│ │ ├── PalmDocCompression.cs
│ │ └── HuffCdicCompression.cs
│ ├── Define
│ │ └── Const.cs
│ ├── PalmDBRecord.cs
│ ├── ContentFile.cs
│ ├── Roler.Toolkit.File.Mobi.csproj
│ ├── Structure.cs
│ ├── Mobi.cs
│ ├── MobiReadingConfiguration.cs
│ ├── Engine
│ │ ├── PalmDOCHeaderEngine.cs
│ │ ├── IndxHeaderEngine.cs
│ │ ├── PalmDBEngine.cs
│ │ ├── ExthHeaderEngine.cs
│ │ ├── RecordEngine.cs
│ │ └── MobiHeaderEngine.cs
│ ├── ExtendMethod.cs
│ └── MobiReader.cs
└── RolerFileToolkit.sln
├── LICENSE.md
├── README.md
└── .gitignore
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/StoreLogo.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/NavInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavInfo
4 | {
5 | public string Text { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavSpan.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavSpan
4 | {
5 | public string Value { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/NavPointLabel.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavPointLabel
4 | {
5 | public string Text { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/LockScreenLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/LockScreenLogo.scale-200.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/IndxHeader/IndexType.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum IndexType : uint
4 | {
5 | Normal,
6 |
7 | Inflections = 2,
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavHead.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavHead
4 | {
5 | public string Name { get; set; }
6 | public string Value { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rolerzhang/RolerFileToolkit/HEAD/RolerFileToolkit/Samples/FileToolkitSample.UWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/NavPointContent.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavPointContent
4 | {
5 | public string Id { get; set; }
6 | public string Source { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Compression/ICompression.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Compression
2 | {
3 | internal interface ICompression
4 | {
5 | byte[] Compress(byte[] bytes);
6 | byte[] Decompress(byte[] bytes);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/MobiHeader/TextEncoding.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum TextEncoding : uint
4 | {
5 | Unkown,
6 |
7 | CP1252 = 1252,
8 |
9 | UTF8 = 65001,
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/Nav.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class Nav
6 | {
7 | public IList NavElements { get; } = new List();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavA.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavA
4 | {
5 | public string Href { get; set; }
6 | public string Title { get; set; }
7 | public string Value { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/Ncx.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class Ncx
4 | {
5 | public string Version { get; set; }
6 | public string Language { get; set; }
7 | public NavMap NavMap { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Container.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class Container
4 | {
5 | public string Namespace { get; set; }
6 | public string FullPath { get; set; }
7 | public string MediaType { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/NavMap.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class NavMap
6 | {
7 | public NavInfo NavInfo { get; set; }
8 | public IList NavPoints { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Define/Const.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Define
2 | {
3 | internal class Const
4 | {
5 | public const int ByteBitLength = 8;
6 | public const int ShortBitLength = 2 * 8;
7 | public const int IntBitLength = 4 * 8;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/Manifest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class Manifest
6 | {
7 | public string Id { get; set; }
8 | public IList Items { get; } = new List();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavOl.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class NavOl
6 | {
7 | public string Id { get; set; }
8 | public bool IsHidden { get; set; }
9 | public IList Items { get; } = new List();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/SpineItemRef.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class SpineItemRef
4 | {
5 | public string IdRef { get; set; }
6 | public string Linear { get; set; }
7 | public string Id { get; set; }
8 | public string Properties { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavLi.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavLi
4 | {
5 | public string Id { get; set; }
6 | public bool IsHidden { get; set; }
7 | public NavA A { get; set; }
8 | public NavSpan Span { get; set; }
9 | public NavOl Ol { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Nav/NavElement.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class NavElement
4 | {
5 | public string Type { get; set; }
6 | public string Id { get; set; }
7 | public bool IsHidden { get; set; }
8 | public NavHead NavHead { get; set; }
9 | public NavOl Ol { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/LinkElement.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class LinkElement
4 | {
5 | public string Href { get; set; }
6 | public string Rel { get; set; }
7 | public string Id { get; set; }
8 | public string Refines { get; set; }
9 | public string MediaType { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Chapter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub
4 | {
5 | public class Chapter
6 | {
7 | public string Title { get; set; }
8 | public string ContentFilePath { get; set; }
9 | public string SecondPath { get; set; }
10 | public IList Chapters { get; } = new List();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/PalmDBRecord.cs:
--------------------------------------------------------------------------------
1 | using Roler.Toolkit.File.Mobi.Entity;
2 |
3 | namespace Roler.Toolkit.File.Mobi
4 | {
5 | internal class PalmDBRecord
6 | {
7 | public PalmDBRecordInfo Info { get; }
8 | public int Length { get; set; }
9 |
10 | public PalmDBRecord(PalmDBRecordInfo info)
11 | {
12 | this.Info = info;
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/DcElement.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | ///
4 | /// The Element of DCMES.
5 | ///
6 | public class DcElement
7 | {
8 | public string Id { get; set; }
9 | public string Language { get; set; }
10 | public string Dir { get; set; }
11 | public string Value { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Compression/NoneCompression.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Compression
2 | {
3 | internal class NoneCompression : ICompression
4 | {
5 | public byte[] Compress(byte[] bytes)
6 | {
7 | return bytes;
8 | }
9 |
10 | public byte[] Decompress(byte[] bytes)
11 | {
12 | return bytes;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/ContentFile.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Roler.Toolkit.File.Mobi
3 | {
4 | public class ContentFile
5 | {
6 | public string MediaType { get; set; }
7 | public string Source { get; private set; }
8 |
9 | public ContentFile(string mediaType, string source)
10 | {
11 | MediaType = mediaType;
12 | Source = source;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/Spine.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class Spine
6 | {
7 | public string Id { get; set; }
8 | public string Toc { get; set; }
9 | public string PageProgressionDirection { get; set; }
10 | public IList Items { get; } = new List();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/ContentFile.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Roler.Toolkit.File.Epub
3 | {
4 | public class ContentFile
5 | {
6 | public string MediaType { get; set; }
7 | public string FilePath { get; private set; }
8 |
9 | public ContentFile(string mediaType, string filePath)
10 | {
11 | MediaType = mediaType;
12 | FilePath = filePath;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Define/Const.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace Roler.Toolkit.File.Epub.Define
4 | {
5 | internal class Const
6 | {
7 | public const string CONTAINER_PATH = @"META-INF/container.xml";
8 |
9 | public readonly static XName ATTRIBUTE_LANGUAGE = XNamespace.Xml + "lang";
10 | public readonly static XNamespace EPUB_NAMESPACE = @"http://www.idpf.org/2007/ops";
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace FileToolkitSample.WPF
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/ManifestItem.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class ManifestItem
4 | {
5 | public string Id { get; set; }
6 | public string Href { get; set; }
7 | public string MediaType { get; set; }
8 | public string Fallback { get; set; }
9 | public string Properties { get; set; }
10 | public string MediaOverlay { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Ncx/NavPoint.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class NavPoint
6 | {
7 | public string Id { get; set; }
8 | public string PlayOrder { get; set; }
9 | public NavPointLabel Label { get; set; }
10 | public NavPointContent Content { get; set; }
11 | public IList Children { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Helper/PathHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Roler.Toolkit.File.Epub.Helper
4 | {
5 | internal static class PathHelper
6 | {
7 | public static string Combine(params string[] paths)
8 | {
9 | var path = Path.Combine(paths);
10 | if (path != null)
11 | {
12 | path = path.Replace(@"\", "/");
13 | }
14 | return path;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/AudiRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class AudiRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'AUDI'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the media data continues to the end of this record.
12 | ///
13 | public byte[] Media { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/VideRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class VideRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'VIDE'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the media data continues to the end of this record.
12 | ///
13 | public byte[] Media { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDOCHeader/EncryptionType.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum EncryptionType : ushort
4 | {
5 | ///
6 | /// No encryption.
7 | ///
8 | None = 0,
9 |
10 | ///
11 | /// Old Mobipocket Encryption.
12 | ///
13 | OldMobi = 1,
14 |
15 | ///
16 | /// Mobipocket Encryption.
17 | ///
18 | Mobi = 2,
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDOCHeader/CompressionType.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum CompressionType : ushort
4 | {
5 | Unkown,
6 |
7 | ///
8 | /// No compression.
9 | ///
10 | No = 1,
11 |
12 | ///
13 | /// PalmDOC compression.
14 | ///
15 | PalmDOC = 2,
16 |
17 | ///
18 | /// HUFF/CDIC compression.
19 | ///
20 | HUFF_CDIC = 17480,
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/Package.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class Package
4 | {
5 | public float Version { get; set; }
6 | public string Identifier { get; set; }
7 | public string Prefix { get; set; }
8 | public string Language { get; set; }
9 | public string Dir { get; set; }
10 | public string Id { get; set; }
11 | public Metadata Metadata { get; set; }
12 | public Manifest Manifest { get; set; }
13 | public Spine Spine { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Structure.cs:
--------------------------------------------------------------------------------
1 | using Roler.Toolkit.File.Epub.Entity;
2 |
3 | namespace Roler.Toolkit.File.Epub
4 | {
5 | public class Structure
6 | {
7 | public Container Container { get; set; }
8 | public Package Package { get; set; }
9 |
10 | ///
11 | /// 获取或设置ncx相关信息,仅在Epub3.0之前有效,3.0以后请使用Nav属性。
12 | ///
13 | public Ncx Ncx { get; set; }
14 |
15 | ///
16 | /// 获取或设置导航文件信息,仅在Epub3.0之后有效,3.0之前请使用Ncx属性。
17 | ///
18 | public Nav Nav { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Roler.Toolkit.File.Mobi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | RolerZhang
6 | .NET Standard Class Library for Mobi File.
7 | Apache-2.0
8 | https://github.com/rolerzhang/RolerFileToolkit
9 | mobi
10 | 1.0.5
11 | 1.0.5.0
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Structure.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi
5 | {
6 | public class Structure
7 | {
8 | public string FullName { get; set; }
9 | public PalmDB PalmDB { get; set; }
10 | public PalmDOCHeader PalmDOCHeader { get; set; }
11 | public MobiHeader MobiHeader { get; set; }
12 | public ExthHeader ExthHeader { get; set; }
13 | public IndxHeader IndxHeader { get; set; }
14 | public FlisRecord FlisRecord { get; set; }
15 | public FcisRecord FcisRecord { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDB/PalmDBRecordAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum PalmDBRecordAttribute : byte
4 | {
5 | Unkown,
6 |
7 | ///
8 | /// Secret record bit.
9 | ///
10 | Secret = 0x10,
11 |
12 | ///
13 | /// Record in use (busy bit).
14 | ///
15 | InUse = 0x20,
16 |
17 | ///
18 | /// Dirty record bit.
19 | ///
20 | Dirty = 0x40,
21 |
22 | ///
23 | /// Delete record on next HotSync.
24 | ///
25 | Delete = 0x80,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Roler.Toolkit.File.Epub.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | RolerZhang
6 | Roler.Toolkit.File.Epub
7 | epub
8 | .NET Standard Class Library for Epub File. support EPUB 3.0
9 | Apache-2.0
10 | https://github.com/rolerzhang/RolerFileToolkit
11 |
12 | 1.0.1
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDB/PalmDBRecordInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class PalmDBRecordInfo
4 | {
5 | ///
6 | /// Gets or sets the offset of record n from the start of the PDB of this record.
7 | ///
8 | public uint Offset { get; set; }
9 |
10 | public PalmDBRecordAttribute Attribute { get; set; }
11 |
12 | ///
13 | /// Gets or sets the unique ID for this record. Often just a sequential count from 0.
14 | ///
15 | public uint UniqueID { get; set; }
16 |
17 | public override string ToString()
18 | {
19 | return $"{UniqueID}, {this.Attribute} , {Offset}";
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/MetaElement.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Epub.Entity
2 | {
3 | public class MetaElement
4 | {
5 | public string Id { get; set; }
6 | public string Language { get; set; }
7 | public string Dir { get; set; }
8 | public string Scheme { get; set; }
9 | public string Property { get; set; }
10 | public string Refines { get; set; }
11 | public string Value { get; set; }
12 |
13 | ///
14 | /// Attribute name, Only Epub2.0
15 | ///
16 | public string Name { get; set; }
17 |
18 | ///
19 | /// Attribute content, Only Epub2.0
20 | ///
21 | public string Content { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Mobi.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Roler.Toolkit.File.Mobi
3 | {
4 | public class Mobi
5 | {
6 | public Structure Structure { get; set; }
7 |
8 | public string Title { get; set; }
9 | public string Creator { get; set; }
10 | public string Publisher { get; set; }
11 | public string Description { get; set; }
12 | public string Subject { get; set; }
13 | public string Date { get; set; }
14 | public string Contributor { get; set; }
15 | public string Rights { get; set; }
16 | public string Type { get; set; }
17 | public string Source { get; set; }
18 | public string Language { get; set; }
19 | public ContentFile Cover { get; set; }
20 |
21 | public string Text { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/CmetRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class CmetRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'CMET'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the value0, fixed value: 0x0000000C.
12 | ///
13 | public uint Value0 { get; set; }
14 |
15 | ///
16 | /// Gets or sets the length of the text.
17 | ///
18 | public uint TextLength { get; set; }
19 |
20 | ///
21 | /// Gets or sets the compilation output text, line endings are CRLF.
22 | ///
23 | public string Text { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/ExtendMethod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Roler.Toolkit.File.Epub.Entity;
4 |
5 | namespace Roler.Toolkit.File.Epub
6 | {
7 | internal static class ExtendMethod
8 | {
9 | private const string XML_1_0_START = "
8 | /// Gets or sets the identifier, always 'EXTH'.
9 | ///
10 | public string Identifier { get; set; }
11 |
12 | ///
13 | /// Gets or sets the length of the EXTH header, including the previous 4 bytes.
14 | ///
15 | public uint Length { get; set; }
16 |
17 | ///
18 | /// Gets or sets the number of records in the EXTH header. the rest of the EXTH header consists of repeated EXTH records to the end of the EXTH length.
19 | ///
20 | public uint RecordCount { get; set; }
21 |
22 | public IList RecordList { get; } = new List();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/SrcsRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class SrcsRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'SRCS'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the value0, fixed value: 0x00000010.
12 | ///
13 | public uint Value0 { get; set; }
14 |
15 | ///
16 | /// Gets or sets the value1, fixed value: 0x0000002f.
17 | ///
18 | public uint Value1 { get; set; }
19 |
20 | ///
21 | /// Gets or sets the value2, fixed value: 0x00000001.
22 | ///
23 | public uint Value2 { get; set; }
24 |
25 | ///
26 | /// Gets or sets the zip archive continues to the end of this record.
27 | ///
28 | public byte[] Zip { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/MobiHeader/MobiType.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum MobiType : uint
4 | {
5 | Unkown,
6 |
7 | ///
8 | /// Mobipocket Book.
9 | ///
10 | Mobipocket = 2,
11 |
12 | ///
13 | /// PalmDoc Book.
14 | ///
15 | PalmDoc = 3,
16 |
17 | ///
18 | /// Audio.
19 | ///
20 | Audio = 4,
21 |
22 | ///
23 | /// mobipocket? generated by kindlegen1.2.
24 | ///
25 | Mobipocket_Kindlegen_1_2 = 232,
26 |
27 | ///
28 | /// generated by kindlegen2.
29 | ///
30 | KF8 = 248,
31 |
32 | News = 257,
33 |
34 | News_Feed = 258,
35 |
36 | News_Magazine = 259,
37 |
38 | PICS = 513,
39 |
40 | WORD = 514,
41 |
42 | XLS = 515,
43 |
44 | PPT = 516,
45 |
46 | TEXT = 517,
47 |
48 | HTML = 518,
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDB/PalmDBAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum PalmDBAttribute : ushort
4 | {
5 | Unkown,
6 |
7 | ///
8 | /// Read-Only.
9 | ///
10 | ReadOnly = 0x0002,
11 |
12 | ///
13 | /// Dirty AppInfoArea.
14 | ///
15 | DirtyAppInfoArea = 0x0004,
16 |
17 | ///
18 | /// Backup this database.
19 | ///
20 | Backup = 0x0008,
21 |
22 | ///
23 | /// Okay to install newer over existing copy, if present on PalmPilot.
24 | ///
25 | OverInstall = 0x0010,
26 |
27 | ///
28 | /// Force the PalmPilot to reset after this database is installed.
29 | ///
30 | ResetAfterInstall = 0x0020,
31 |
32 | ///
33 | /// Don't allow copy of file to be beamed to other Pilot.
34 | ///
35 | DisallowCopy = 0x0040,
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 RolerZhang
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 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/ExthHeader/ExthRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class ExthRecord
4 | {
5 | ///
6 | /// Gets or sets the Exth Record type. Just a number identifying what's stored in the record.
7 | ///
8 | public ExthRecordType Type { get; set; }
9 |
10 | ///
11 | /// Gets or sets the length of the EXTH record = L , including the 8 bytes in the type and length fields.
12 | ///
13 | public uint Length { get; set; }
14 |
15 | ///
16 | /// Gets or sets the Data of the EXTH record.
17 | ///
18 | public byte[] Data { get; set; }
19 |
20 | public string DataAsString()
21 | {
22 | if (this.Data != null)
23 | {
24 | return System.Text.Encoding.UTF8.GetString(this.Data);
25 | }
26 | return null;
27 | }
28 |
29 | public override string ToString()
30 | {
31 | return $"{this.Type} : {this.Data}";
32 | }
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FileToolkitSample.UWP")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FileToolkitSample.UWP")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Version information for an assembly consists of the following four values:
18 | //
19 | // Major Version
20 | // Minor Version
21 | // Build Number
22 | // Revision
23 | //
24 | // You can specify all the values or you can default the Build and Revision Numbers
25 | // by using the '*' as shown below:
26 | // [assembly: AssemblyVersion("1.0.*")]
27 | [assembly: AssemblyVersion("1.0.0.0")]
28 | [assembly: AssemblyFileVersion("1.0.0.0")]
29 | [assembly: ComVisible(false)]
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FileToolkitSample.WPF.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Epub.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub
4 | {
5 | public class Epub
6 | {
7 | public Structure Structure { get; set; }
8 |
9 | public string Contributor { get; set; }
10 | public string Coverage { get; set; }
11 | public string Creator { get; set; }
12 | public string Date { get; set; }
13 | public string Description { get; set; }
14 | public string Format { get; set; }
15 | public string Identifier { get; set; }
16 | public string Language { get; set; }
17 | public string Publisher { get; set; }
18 | public string Relation { get; set; }
19 | public string Rights { get; set; }
20 | public string Source { get; set; }
21 | public string Subject { get; set; }
22 | public string Title { get; set; }
23 | public string Type { get; set; }
24 | public ContentFile Cover { get; set; }
25 | public IList Chapters { get; } = new List();
26 | public IList ReadingFiles { get; } = new List();
27 | public IList AllFiles { get; } = new List();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Compression/BitReader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Mobi
4 | {
5 | internal class BitReader
6 | {
7 | private readonly IList _data;
8 | private readonly int _nbits;
9 |
10 | private uint _pos = 0;
11 |
12 | public bool IsEnd => this._nbits > this._pos;
13 |
14 | public BitReader(byte[] bytes)
15 | {
16 | this._data = new List(bytes)
17 | {
18 | 0,
19 | 0,
20 | 0,
21 | 0
22 | };
23 | this._nbits = (this._data.Count - 4) * 8;
24 | }
25 |
26 | public ulong Peek(ulong n)
27 | {
28 | ulong r = 0;
29 | ulong g = 0;
30 | while (g < n)
31 | {
32 | r = (r << 8) | (char)(_data[(int)((long)(_pos + g >> 3))]);
33 | g = g + 8 - ((_pos + g) & 7);
34 | }
35 | return r >> (int)((long)(g - n)) & ((ulong)(1) << (int)n) - 1;
36 | }
37 |
38 | public bool Eat(uint n)
39 | {
40 | _pos += n;
41 | return _pos <= _nbits;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDOCHeader/PalmDOCHeader.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class PalmDOCHeader
4 | {
5 | public CompressionType Compression { get; set; }
6 |
7 | ///
8 | /// Always zero.
9 | ///
10 | public short Unused { get; }
11 |
12 | ///
13 | /// Gets or sets the uncompressed length of the entire text of the book.
14 | ///
15 | public uint TextLength { get; set; }
16 |
17 | ///
18 | /// Gets or sets the number of PDB records used for the text of the book.
19 | ///
20 | public ushort RecordCount { get; set; }
21 |
22 | ///
23 | /// Gets or sets the maximum size of each record containing text, always 4096.
24 | ///
25 | public ushort RecordSize { get; set; }
26 |
27 | ///
28 | /// Gets or sets the current reading position, as an offset into the uncompressed text.
29 | ///
30 | public uint CurrentPosition { get; set; }
31 |
32 | ///
33 | /// Gets or sets the encryption type, Only HUFF/CDIC compression.
34 | ///
35 | public EncryptionType Encryption { get; set; }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Properties/Default.rd.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Entity/Opf/Metadata.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Roler.Toolkit.File.Epub.Entity
4 | {
5 | public class Metadata
6 | {
7 | public IList Contributors { get; } = new List();
8 | public IList Coverages { get; } = new List();
9 | public IList Creators { get; } = new List();
10 | public DcElement Date { get; set; }
11 | public IList Descriptions { get; } = new List();
12 | public IList Formats { get; } = new List();
13 | public IList Identifiers { get; } = new List();
14 | public IList Languages { get; } = new List();
15 | public IList Publishers { get; } = new List();
16 | public IList Relations { get; } = new List();
17 | public IList Rights { get; } = new List();
18 | public IList Sources { get; } = new List();
19 | public IList Subjects { get; } = new List();
20 | public IList Titles { get; } = new List();
21 | public IList Types { get; } = new List();
22 | public IList Metas { get; } = new List();
23 | public IList Links { get; } = new List();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Engine/ContainerEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using System.Xml.Linq;
4 | using Roler.Toolkit.File.Epub.Entity;
5 |
6 | namespace Roler.Toolkit.File.Epub.Engine
7 | {
8 | internal static class ContainerEngine
9 | {
10 | #region Const String
11 |
12 | private const string FULLPATH = "full-path";
13 | private const string MEDIATYPE = "media-type";
14 | private const string ROOTFILE = "rootfile";
15 |
16 | #endregion
17 |
18 | public static Container Read(Stream stream)
19 | {
20 | Container result = null;
21 | using (var streamReader = new StreamReader(stream))
22 | {
23 | string xmlStr = streamReader.ReadToEnd();
24 | var document = XElement.Parse(xmlStr.FixXml());
25 |
26 | var xNamespace = document.GetDefaultNamespace();
27 | result = new Container
28 | {
29 | Namespace = xNamespace.NamespaceName
30 | };
31 |
32 | var xElement = document.Descendants(xNamespace + ROOTFILE).FirstOrDefault();
33 | if (xElement != null)
34 | {
35 | result.FullPath = xElement.Attribute(FULLPATH).Value;
36 | result.MediaType = xElement.Attribute(MEDIATYPE).Value;
37 | }
38 | }
39 | return result;
40 | }
41 |
42 | public static void Write(Container file, Stream stream)
43 | {
44 | }
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/MobiReadingConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Roler.Toolkit.File.Mobi.Engine;
3 | using Roler.Toolkit.File.Mobi.Entity;
4 |
5 | namespace Roler.Toolkit.File.Mobi
6 | {
7 | public class MobiReadingConfiguration
8 | {
9 | public virtual int FindFirstTextRecordIndex(MobiHeader mobiHeader)
10 | {
11 | if (mobiHeader is null)
12 | {
13 | throw new ArgumentNullException(nameof(mobiHeader));
14 | }
15 |
16 | return mobiHeader.FirstContentRecordOffset > 0 ? mobiHeader.FirstContentRecordOffset : 1;
17 | }
18 |
19 | public virtual long FindFirstNonTextRecordIndex(MobiHeader mobiHeader, long recordCount)
20 | {
21 | if (mobiHeader is null)
22 | {
23 | throw new ArgumentNullException(nameof(mobiHeader));
24 | }
25 |
26 | long result;
27 | if (mobiHeader.FirstNonBookIndex != MobiHeaderEngine.UnavailableIndex &&
28 | mobiHeader.FirstNonBookIndex < recordCount)
29 | {
30 | result = mobiHeader.FirstNonBookIndex;
31 | }
32 | else
33 | {
34 | result = Math.Min(mobiHeader.LastContentRecordOffset, mobiHeader.INDXRecordOffset);
35 | result = Math.Min(result, mobiHeader.FLISRecordOffset);
36 | result = Math.Min(result, mobiHeader.FCISRecordOffset);
37 | result = Math.Min(result, recordCount);
38 | }
39 | return result;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/PalmDB/PalmDB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Roler.Toolkit.File.Mobi.Entity
6 | {
7 | public class PalmDB
8 | {
9 | ///
10 | /// Gets or sets the name of PDB.
11 | ///
12 | public string Name { get; set; }
13 |
14 | ///
15 | /// Gets or sets attributes of PDB.
16 | ///
17 | public PalmDBAttribute Attribute { get; set; }
18 |
19 | public ushort Version { get; set; }
20 |
21 | ///
22 | /// See above table. (For Applications this data will be 'appl')
23 | ///
24 | public uint Type { get; set; }
25 |
26 | ///
27 | /// See above table. This program will be launched if the file is tapped.
28 | ///
29 | public uint Creator { get; set; }
30 |
31 | ///
32 | /// used internally to identify record.
33 | ///
34 | public uint UniqueIDseed { get; set; }
35 |
36 | ///
37 | /// Only used when in-memory on Palm OS. Always set to zero in stored files.
38 | ///
39 | public uint NextRecordListID { get; set; }
40 |
41 | ///
42 | /// Gets or sets the number of records in the file.
43 | ///
44 | public ushort RecordCount { get; set; }
45 |
46 | public IList RecordInfoList { get; } = new List();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Build Status
2 | | Target | Branch |Recommended package version |
3 | | ------ | ------ | ------ |
4 | | Roler.Toolkit.File.Epub | master | [1.0.1](https://www.nuget.org/packages/Roler.Toolkit.File.Epub) |
5 | | Roler.Toolkit.File.Mobi | master | [1.0.5](https://www.nuget.org/packages/Roler.Toolkit.File.Mobi) |
6 |
7 | ## Sample Code
8 | ### Epub
9 |
10 | ```csharp
11 | using (var epubReader = new EpubReader(stream))
12 | {
13 | if (epubReader.TryRead(out Epub epub))
14 | {
15 | var title = epub.Title;
16 | var creator = epub.Creator;
17 | var publisher = epub.Publisher;
18 | var description = epub.Description;
19 | //...
20 |
21 | ContentFile cover = epub.Cover;
22 | IList chapters = epub.Chapters;
23 | IList allFiles = epub.AllFiles;
24 | IList ReadingFiles = epub.ReadingFiles; //Ordered files for read.
25 |
26 | Structure structure = epub.Structure; //Structure inside epub file.
27 | float version = structure.Package.Version; //The version of epub file.
28 |
29 | Stream coverStream = epubReader.ReadContentFile(cover.FilePath); //read content file by file path.
30 | }
31 | }
32 | ```
33 |
34 | ### Mobi
35 |
36 | ```csharp
37 | using (var mobiReader = new MobiReader(fileStream))
38 | {
39 | var mobi = mobiReader.Read();
40 |
41 | var creator = mobi.Creator;
42 | var publisher = mobi.Publisher;
43 | var description = mobi.Description;
44 | //...
45 |
46 | Structure structure = mobi.Structure; //Structure inside mobi file.
47 |
48 | string text = mobi.Text; //full text content.
49 | }
50 | ```
51 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
32 |
34 |
35 |
37 |
38 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
15 |
16 |
17 | FileToolkitSample.UWP
18 | zhang
19 | Assets\StoreLogo.png
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/IndxHeader/IndxHeader.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class IndxHeader
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'INDX'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the length of the INDX header, including the previous 4 bytes.
12 | ///
13 | public uint Length { get; set; }
14 |
15 | ///
16 | /// Gets or sets the type of the index.
17 | ///
18 | public IndexType IndexType { get; set; }
19 |
20 | ///
21 | /// Gets or sets the offset to the IDXT section.
22 | ///
23 | public uint IdxtStart { get; set; }
24 |
25 | ///
26 | /// Gets or sets the number of index records.
27 | ///
28 | public uint IndexCount { get; set; }
29 |
30 | ///
31 | /// Gets or sets the number of index records.
32 | ///
33 | public TextEncoding IndexEncoding { get; set; }
34 |
35 | ///
36 | /// Gets or sets the language code of the index.
37 | ///
38 | public string IndexLanguage { get; set; }
39 |
40 | ///
41 | /// Gets or sets the number of index entries.
42 | ///
43 | public uint TotalIndexCount { get; set; }
44 |
45 | ///
46 | /// Gets or sets the offset to the ORDT section.
47 | ///
48 | public uint OrdtStart { get; set; }
49 |
50 | ///
51 | /// Gets or sets the offset to the LIGT section.
52 | ///
53 | public uint LigtStart { get; set; }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/PalmDOCHeaderEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi.Engine
5 | {
6 | internal static class PalmDOCHeaderEngine
7 | {
8 | #region Const String
9 |
10 | #endregion
11 |
12 | public static PalmDOCHeader Read(Stream stream, long offset)
13 | {
14 | PalmDOCHeader result = new PalmDOCHeader();
15 | stream.Seek(offset, SeekOrigin.Begin);
16 | if (stream.TryReadUshort(out ushort compression))
17 | {
18 | result.Compression = (CompressionType)compression;
19 | }
20 | stream.Seek(2, SeekOrigin.Current);
21 | if (stream.TryReadUint(out uint textLength))
22 | {
23 | result.TextLength = textLength;
24 | }
25 | if (stream.TryReadUshort(out ushort recordCount))
26 | {
27 | result.RecordCount = recordCount;
28 | }
29 | if (stream.TryReadUshort(out ushort recordSize))
30 | {
31 | result.RecordSize = recordSize;
32 | }
33 |
34 | if (result.Compression == CompressionType.HUFF_CDIC)
35 | {
36 | if (stream.TryReadUshort(out ushort encryption))
37 | {
38 | result.Encryption = (EncryptionType)encryption;
39 | }
40 | stream.Seek(2, SeekOrigin.Current);
41 | }
42 | else
43 | {
44 | if (stream.TryReadUint(out uint currentPosition))
45 | {
46 | result.CurrentPosition = currentPosition;
47 | }
48 | }
49 |
50 |
51 | return result;
52 | }
53 |
54 | public static void Write(PalmDOCHeader file, Stream stream)
55 | {
56 | }
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/FlisRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class FlisRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'FLIS'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the value0, fixed value: 8.
12 | ///
13 | public uint Value0 { get; set; }
14 |
15 | ///
16 | /// Gets or sets the value1, fixed value: 65.
17 | ///
18 | public ushort Value1 { get; set; }
19 |
20 | ///
21 | /// Gets or sets the value2, fixed value: 0.
22 | ///
23 | public ushort Value2 { get; set; }
24 |
25 | ///
26 | /// Gets or sets the value3, fixed value: 0.
27 | ///
28 | public uint Value3 { get; set; }
29 |
30 | ///
31 | /// Gets or sets the value4, fixed value: -1 (0xFFFFFFFF).
32 | ///
33 | public uint Value4 { get; set; }
34 |
35 | ///
36 | /// Gets or sets the value5, fixed value: 1.
37 | ///
38 | public ushort Value5 { get; set; }
39 |
40 | ///
41 | /// Gets or sets the value6, fixed value: 3.
42 | ///
43 | public ushort Value6 { get; set; }
44 |
45 | ///
46 | /// Gets or sets the value7, fixed value: 3.
47 | ///
48 | public uint Value7 { get; set; }
49 |
50 | ///
51 | /// Gets or sets the value8, fixed value: 1.
52 | ///
53 | public uint Value8 { get; set; }
54 |
55 | ///
56 | /// Gets or sets the value9, fixed value: -1 (0xFFFFFFFF).
57 | ///
58 | public uint Value9 { get; set; }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/ExthHeader/ExthRecordType.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public enum ExthRecordType : uint
4 | {
5 | Unkown,
6 | DRMServerId = 1,
7 | DRMCommerceId = 2,
8 | DRMEbookbaseBookId = 3,
9 | Author = 100,
10 | Publisher,
11 | Imprint,
12 | Description,
13 | ISBN,
14 | Subject,
15 | PublishingDate,
16 | Review,
17 | Contributor,
18 | Rights,
19 | SubjectCode,
20 | Type,
21 | Source,
22 | ASIN,
23 | VersionNumber,
24 | Sample,
25 | StartReading,
26 | Adult,
27 | RetailPrice,
28 | RetailPriceCurrency,
29 | KF8BoundaryOffset = 121,
30 | ResourceCount = 125,
31 | KF8CoverUri = 129,
32 | Unknown1 = 131,
33 | DictionaryShortName = 200,
34 | CoverOffset,
35 | thumbOffset,
36 | HasFakeCover,
37 | CreatorSoftware,
38 | CreatorMajorVersion,
39 | CreatorMinorVersion,
40 | CreatorBuildVersion,
41 | Watermark,
42 | TamperProofKeys,
43 | FontSignature = 300,
44 | ClippingLimit = 401,
45 | PublisherLimit,
46 | Unknown2,
47 |
48 | ///
49 | /// 1 - Text to Speech disabled; 0 - Text to Speech enabled
50 | ///
51 | TTSFlag,
52 |
53 | Unknown3,
54 | RentBorrowExpirationDate,
55 | Unknown4 = 407,
56 | Unknown5 = 450,
57 | Unknown6,
58 | Unknown7,
59 | Unknown8,
60 |
61 | ///
62 | /// PDOC - Personal Doc; EBOK - ebook; EBSP - ebook sample;
63 | ///
64 | CdeType = 501,
65 |
66 | LastUpdateTime,
67 | UpdatedTitle,
68 | ASIN_Copy,
69 | Language = 524,
70 | WritingMode,
71 | CreatorBuildNumber = 535,
72 | Unknown9,
73 | Unknown10 = 542,
74 | InMemory = 547,
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Roler.Toolkit.File.Epub;
4 | using Windows.Storage.Pickers;
5 | using Windows.UI.Xaml;
6 | using Windows.UI.Xaml.Controls;
7 |
8 | namespace FileToolkitSample.UWP
9 | {
10 | public sealed partial class MainPage : Page
11 | {
12 | Stream fileStream;
13 | EpubReader epubReader;
14 |
15 | public MainPage()
16 | {
17 | this.InitializeComponent();
18 | }
19 |
20 | private async void OpenEpubButton_Click(object sender, RoutedEventArgs e)
21 | {
22 | var picker = new FileOpenPicker
23 | {
24 | SuggestedStartLocation = PickerLocationId.ComputerFolder
25 | };
26 | picker.FileTypeFilter.Add(".epub");
27 | var file = await picker.PickSingleFileAsync();
28 |
29 | if (file != null)
30 | {
31 | this.fileStream = await file.OpenStreamForReadAsync();
32 | {
33 | epubReader = new EpubReader(fileStream);
34 | {
35 | if (epubReader.TryRead(out Epub epub))
36 | {
37 | this.listView.ItemsSource = epub.ReadingFiles;
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
44 | private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
45 | {
46 | if (e.AddedItems.Count > 0 && e.AddedItems[0] is ContentFile contentFile)
47 | {
48 | if (this.epubReader != null)
49 | {
50 | var content = this.epubReader.ReadContentAsText(contentFile.FilePath);
51 | this.textBlock.Text = content;
52 | }
53 | }
54 | }
55 |
56 | ~MainPage()
57 | {
58 | this.epubReader?.Dispose();
59 | this.fileStream?.Dispose();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/FcisRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Roler.Toolkit.File.Mobi.Entity
2 | {
3 | public class FcisRecord
4 | {
5 | ///
6 | /// Gets or sets the identifier, always 'FCIS'.
7 | ///
8 | public string Identifier { get; set; }
9 |
10 | ///
11 | /// Gets or sets the value0, fixed value: 20.
12 | ///
13 | public uint Value0 { get; set; }
14 |
15 | ///
16 | /// Gets or sets the value1, fixed value: 16.
17 | ///
18 | public uint Value1 { get; set; }
19 |
20 | ///
21 | /// Gets or sets the value2, fixed value: 1.
22 | ///
23 | public uint Value2 { get; set; }
24 |
25 | ///
26 | /// Gets or sets the value3, fixed value: 0.
27 | ///
28 | public uint Value3 { get; set; }
29 |
30 | ///
31 | /// Gets or sets the value4, text length (the same value as "text length" in the PalmDoc header).
32 | ///
33 | public uint Value4 { get; set; }
34 |
35 | ///
36 | /// Gets or sets the value5, fixed value: 0.
37 | ///
38 | public uint Value5 { get; set; }
39 |
40 | ///
41 | /// Gets or sets the value6, fixed value: 32.
42 | ///
43 | public uint Value6 { get; set; }
44 |
45 | ///
46 | /// Gets or sets the value7, fixed value: 8.
47 | ///
48 | public uint Value7 { get; set; }
49 |
50 | ///
51 | /// Gets or sets the value8, fixed value: 1.
52 | ///
53 | public ushort Value8 { get; set; }
54 |
55 | ///
56 | /// Gets or sets the value9, fixed value: 1.
57 | ///
58 | public ushort Value9 { get; set; }
59 |
60 | ///
61 | /// Gets or sets the value10, fixed value: 0.
62 | ///
63 | public uint Value10 { get; set; }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("FileToolkitSample.WPF")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("FileToolkitSample.WPF")]
15 | [assembly: AssemblyCopyright("Copyright © 2019")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Windows;
3 | using Microsoft.Win32;
4 | using Roler.Toolkit.File.Epub;
5 | using Roler.Toolkit.File.Mobi;
6 |
7 | namespace FileToolkitSample.WPF
8 | {
9 | public partial class MainWindow : Window
10 | {
11 | FileStream fileStream;
12 | EpubReader epubReader;
13 |
14 | public MainWindow()
15 | {
16 | InitializeComponent();
17 | }
18 |
19 | private void OpenEpubButton_Click(object sender, RoutedEventArgs e)
20 | {
21 | var openFileDialog = new OpenFileDialog
22 | {
23 | Filter = "epub file|*.epub"
24 | };
25 | if (openFileDialog.ShowDialog() == true)
26 | {
27 | this.fileStream?.Dispose();
28 | this.epubReader?.Dispose();
29 |
30 | var filePath = openFileDialog.FileName;
31 | fileStream = File.OpenRead(filePath);
32 | {
33 | epubReader = new EpubReader(fileStream);
34 | {
35 | var epub = epubReader.Read();
36 | this.listView.ItemsSource = epub.ReadingFiles;
37 | }
38 | }
39 | }
40 | }
41 |
42 | private void ListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
43 | {
44 | if (e.AddedItems.Count > 0 && e.AddedItems[0] is Roler.Toolkit.File.Epub.ContentFile contentFile)
45 | {
46 | if (this.epubReader != null)
47 | {
48 | var content = this.epubReader.ReadContentAsText(contentFile.FilePath);
49 | this.textBlock.Text = content;
50 | }
51 | }
52 | }
53 |
54 | ~MainWindow()
55 | {
56 | this.epubReader?.Dispose();
57 | this.fileStream?.Dispose();
58 | }
59 |
60 | private void OpenMobiButton_Click(object sender, RoutedEventArgs e)
61 | {
62 | var openFileDialog = new OpenFileDialog
63 | {
64 | Filter = "mobi file|*.mobi"
65 | };
66 | if (openFileDialog.ShowDialog() == true)
67 | {
68 |
69 | var filePath = openFileDialog.FileName;
70 | fileStream = File.OpenRead(filePath);
71 | {
72 | using (var mobiReader = new MobiReader(fileStream))
73 | {
74 | var mobi = mobiReader.Read();
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FileToolkitSample.WPF.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FileToolkitSample.WPF.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/IndxHeaderEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi.Engine
5 | {
6 | internal static class IndxHeaderEngine
7 | {
8 | #region Const String
9 |
10 | public const string Identifier = "INDX";
11 |
12 | #endregion
13 |
14 | public static bool TryRead(Stream stream, long offset, out IndxHeader indxHeader)
15 | {
16 | bool result = false;
17 | indxHeader = null;
18 | stream.Seek(offset, SeekOrigin.Begin);
19 | if (stream.CheckStart(4, Identifier))
20 | {
21 | indxHeader = Read(stream, offset);
22 | result = true;
23 | }
24 |
25 | if (!result)
26 | {
27 | stream.Seek(offset, SeekOrigin.Begin);
28 | }
29 |
30 | return result;
31 | }
32 |
33 | public static IndxHeader Read(Stream stream, long offset)
34 | {
35 | IndxHeader result = new IndxHeader();
36 | stream.Seek(offset, SeekOrigin.Begin);
37 | if (stream.TryReadString(4, out string identifier))
38 | {
39 | result.Identifier = identifier;
40 | }
41 | if (stream.TryReadUint(out uint length))
42 | {
43 | result.Length = length;
44 | }
45 | if (stream.TryReadUint(out uint indexType))
46 | {
47 | result.IndexType = (IndexType)indexType;
48 | }
49 |
50 | stream.Seek(8, SeekOrigin.Current); //skip 8 unknown bytes.
51 |
52 | if (stream.TryReadUint(out uint idxtStart))
53 | {
54 | result.IdxtStart = idxtStart;
55 | }
56 | if (stream.TryReadUint(out uint indexCount))
57 | {
58 | result.IndexCount = indexCount;
59 | }
60 | if (stream.TryReadUint(out uint indexEncoding))
61 | {
62 | result.IndexEncoding = (TextEncoding)indexEncoding;
63 | }
64 | if (stream.TryReadString(4, out string indexLanguage))
65 | {
66 | result.IndexLanguage = indexLanguage;
67 | }
68 | if (stream.TryReadUint(out uint totalIndexCount))
69 | {
70 | result.TotalIndexCount = totalIndexCount;
71 | }
72 | if (stream.TryReadUint(out uint ordtStart))
73 | {
74 | result.OrdtStart = ordtStart;
75 | }
76 | if (stream.TryReadUint(out uint ligtStart))
77 | {
78 | result.LigtStart = ligtStart;
79 | }
80 |
81 | stream.Seek(8, SeekOrigin.Current); //skip 8 unknown bytes.
82 | stream.Seek(offset + length, SeekOrigin.Begin); //skip to end.
83 |
84 | return result;
85 | }
86 |
87 | public static void Write(IndxHeader file, Stream stream)
88 | {
89 | }
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/PalmDBEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using Roler.Toolkit.File.Mobi.Entity;
4 |
5 | namespace Roler.Toolkit.File.Mobi.Engine
6 | {
7 | internal static class PalmDBEngine
8 | {
9 | #region Const
10 |
11 | private const int HeaderByteLength = 72;
12 | private const int NameByteLength = 32;
13 |
14 | #endregion
15 |
16 | public static PalmDB Read(Stream stream)
17 | {
18 | PalmDB result = null;
19 | stream.Seek(0, SeekOrigin.Begin);
20 | byte[] buffer = new byte[NameByteLength];
21 | if (stream.Read(buffer, 0, NameByteLength) == NameByteLength)
22 | {
23 | result = new PalmDB { Name = Encoding.UTF8.GetString(buffer) };
24 | if (stream.TryReadUshort(out ushort attribute))
25 | {
26 | result.Attribute = (PalmDBAttribute)attribute;
27 | }
28 | if (stream.TryReadUshort(out ushort version))
29 | {
30 | result.Version = version;
31 | }
32 | stream.Seek(4 * 6, SeekOrigin.Current);
33 |
34 | if (stream.TryReadUint(out uint type))
35 | {
36 | result.Type = type;
37 | }
38 | if (stream.TryReadUint(out uint creator))
39 | {
40 | result.Creator = creator;
41 | }
42 | if (stream.TryReadUint(out uint uniqueIDseed))
43 | {
44 | result.UniqueIDseed = uniqueIDseed;
45 | }
46 | if (stream.TryReadUint(out uint nextRecordListID))
47 | {
48 | result.NextRecordListID = nextRecordListID;
49 | }
50 | if (stream.TryReadUshort(out ushort recordCount))
51 | {
52 | result.RecordCount = recordCount;
53 | }
54 |
55 | for (ushort i = 0; i < recordCount; i++)
56 | {
57 | var recordInfo = new PalmDBRecordInfo();
58 | if (stream.TryReadUint(out uint recordInfoOffset))
59 | {
60 | recordInfo.Offset = recordInfoOffset;
61 | }
62 | if (stream.TryReadByte(out byte recordInfoAttribute))
63 | {
64 | recordInfo.Attribute = (PalmDBRecordAttribute)recordInfoAttribute;
65 | }
66 | var recordUniqueIDBuff = new byte[3];
67 | if (stream.Read(recordUniqueIDBuff, 0, 3) == 3)
68 | {
69 | recordInfo.UniqueID = recordUniqueIDBuff.ToUInt32();
70 | }
71 | result.RecordInfoList.Add(recordInfo);
72 | }
73 | }
74 |
75 | return result;
76 | }
77 |
78 | public static void Write(PalmDB file, Stream stream)
79 | {
80 | }
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/ExthHeaderEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi.Engine
5 | {
6 | internal static class ExthHeaderEngine
7 | {
8 | #region Const String
9 |
10 | public const string Identifier = "EXTH";
11 | public const uint UnavailableIndex = 0xFFFFFFFF;
12 |
13 | #endregion
14 |
15 | public static bool TryRead(Stream stream, long offset, out ExthHeader exthHeader)
16 | {
17 | bool result = false;
18 | exthHeader = null;
19 | stream.Seek(offset, SeekOrigin.Begin);
20 | if (stream.CheckStart(4, Identifier))
21 | {
22 | exthHeader = Read(stream, offset);
23 | result = true;
24 | }
25 |
26 | if (!result)
27 | {
28 | stream.Seek(offset, SeekOrigin.Begin);
29 | }
30 |
31 | return result;
32 | }
33 |
34 | public static ExthHeader Read(Stream stream, long offset)
35 | {
36 | ExthHeader result = new ExthHeader();
37 | stream.Seek(offset, SeekOrigin.Begin);
38 | if (stream.TryReadString(4, out string identifier))
39 | {
40 | result.Identifier = identifier;
41 | }
42 | if (stream.TryReadUint(out uint length))
43 | {
44 | result.Length = length;
45 | }
46 | if (stream.TryReadUint(out uint count))
47 | {
48 | result.RecordCount = count;
49 | }
50 |
51 | for (int i = 0; i < count; i++)
52 | {
53 | var exthRecord = ReadRecord(stream, stream.Position);
54 | if (exthRecord != null)
55 | {
56 | result.RecordList.Add(exthRecord);
57 | }
58 | }
59 |
60 | uint padding = (4 - length % 4) % 4;
61 | stream.Seek(offset + length + padding, SeekOrigin.Begin); //skip to end, included padding
62 |
63 | return result;
64 | }
65 |
66 | public static void Write(ExthHeader file, Stream stream)
67 | {
68 | }
69 |
70 | private static ExthRecord ReadRecord(Stream stream, long offset)
71 | {
72 | ExthRecord result = new ExthRecord();
73 | stream.Seek(offset, SeekOrigin.Begin);
74 |
75 | if (stream.TryReadUint(out uint recordType))
76 | {
77 | result.Type = (ExthRecordType)recordType;
78 | }
79 | if (stream.TryReadUint(out uint length))
80 | {
81 | result.Length = length;
82 | }
83 |
84 | var dataBytesLength = (int)length - 8;
85 | if (dataBytesLength > 0)
86 | {
87 | if (stream.TryReadBytes(dataBytesLength, out byte[] data))
88 | {
89 | result.Data = data;
90 | }
91 | }
92 |
93 | stream.Seek(offset + length, SeekOrigin.Begin); //skip to end.
94 | return result;
95 | }
96 |
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.InteropServices.WindowsRuntime;
6 | using Windows.ApplicationModel;
7 | using Windows.ApplicationModel.Activation;
8 | using Windows.Foundation;
9 | using Windows.Foundation.Collections;
10 | using Windows.UI.Xaml;
11 | using Windows.UI.Xaml.Controls;
12 | using Windows.UI.Xaml.Controls.Primitives;
13 | using Windows.UI.Xaml.Data;
14 | using Windows.UI.Xaml.Input;
15 | using Windows.UI.Xaml.Media;
16 | using Windows.UI.Xaml.Navigation;
17 |
18 | namespace FileToolkitSample.UWP
19 | {
20 | ///
21 | /// Provides application-specific behavior to supplement the default Application class.
22 | ///
23 | sealed partial class App : Application
24 | {
25 | ///
26 | /// Initializes the singleton application object. This is the first line of authored code
27 | /// executed, and as such is the logical equivalent of main() or WinMain().
28 | ///
29 | public App()
30 | {
31 | this.InitializeComponent();
32 | this.Suspending += OnSuspending;
33 | }
34 |
35 | ///
36 | /// Invoked when the application is launched normally by the end user. Other entry points
37 | /// will be used such as when the application is launched to open a specific file.
38 | ///
39 | /// Details about the launch request and process.
40 | protected override void OnLaunched(LaunchActivatedEventArgs e)
41 | {
42 | Frame rootFrame = Window.Current.Content as Frame;
43 |
44 | // Do not repeat app initialization when the Window already has content,
45 | // just ensure that the window is active
46 | if (rootFrame == null)
47 | {
48 | // Create a Frame to act as the navigation context and navigate to the first page
49 | rootFrame = new Frame();
50 |
51 | rootFrame.NavigationFailed += OnNavigationFailed;
52 |
53 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
54 | {
55 | //TODO: Load state from previously suspended application
56 | }
57 |
58 | // Place the frame in the current Window
59 | Window.Current.Content = rootFrame;
60 | }
61 |
62 | if (e.PrelaunchActivated == false)
63 | {
64 | if (rootFrame.Content == null)
65 | {
66 | // When the navigation stack isn't restored navigate to the first page,
67 | // configuring the new page by passing required information as a navigation
68 | // parameter
69 | rootFrame.Navigate(typeof(MainPage), e.Arguments);
70 | }
71 | // Ensure the current window is active
72 | Window.Current.Activate();
73 | }
74 | }
75 |
76 | ///
77 | /// Invoked when Navigation to a certain page fails
78 | ///
79 | /// The Frame which failed navigation
80 | /// Details about the navigation failure
81 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
82 | {
83 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
84 | }
85 |
86 | ///
87 | /// Invoked when application execution is being suspended. Application state is saved
88 | /// without knowing whether the application will be terminated or resumed with the contents
89 | /// of memory still intact.
90 | ///
91 | /// The source of the suspend request.
92 | /// Details about the suspend request.
93 | private void OnSuspending(object sender, SuspendingEventArgs e)
94 | {
95 | var deferral = e.SuspendingOperation.GetDeferral();
96 | //TODO: Save application state and stop any background activity
97 | deferral.Complete();
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Compression/PalmDocCompression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Roler.Toolkit.File.Mobi.Compression
6 | {
7 | ///
8 | /// PalmDoc byte pair compression (LZ77).
9 | ///
10 | internal class PalmDocCompression : ICompression
11 | {
12 | #region Const
13 |
14 | private const int BlockSize = 4096;
15 |
16 | #endregion
17 |
18 | public byte[] Compress(byte[] bytes)
19 | {
20 | if (bytes is null)
21 | {
22 | throw new ArgumentNullException(nameof(bytes));
23 | }
24 | throw new NotImplementedException();
25 | }
26 |
27 |
28 | ///
29 | /// Decompress:
30 | /// 0x00: "1 literal" copy that byte unmodified to the decompressed stream.
31 | /// 0x09 to 0x7f: "1 literal" copy that byte unmodified to the decompressed stream.
32 | /// 0x01 to 0x08: "literals": the byte is interpreted as a count from 1 to 8, and that many literals are copied unmodified from the compressed stream to the decompressed stream.
33 | /// 0x80 to 0xbf: "length, distance" pair: the 2 leftmost bits of this byte ('10') are discarded, and the following 6 bits are combined with the 8 bits of the next byte to make a 14 bit "distance, length" item.Those 14 bits are broken into 11 bits of distance backwards from the current location in the uncompressed text, and 3 bits of length to copy from that point(copying n+3 bytes, 3 to 10 bytes).
34 | /// 0xc0 to 0xff: "byte pair": this byte is decoded into 2 characters: a space character, and a letter formed from this byte XORed with 0x80.
35 | ///
36 | public byte[] Decompress(byte[] bytes)
37 | {
38 | if (bytes is null)
39 | {
40 | throw new ArgumentNullException(nameof(bytes));
41 | }
42 |
43 | IList blockBuilder = new List();
44 | IList dataTemp = new List(bytes) { 0 };
45 | int pos = 0;
46 | IList temps = new List();
47 |
48 | while (pos < dataTemp.Count && blockBuilder.Count < BlockSize)
49 | {
50 | byte ab = dataTemp[pos++];
51 | if (ab == 0x00 || (ab >= 0x09 && ab <= 0x7f))
52 | {
53 | blockBuilder.Add(ab);
54 | }
55 | else if (ab >= 0x01 && ab <= 0x08)
56 | {
57 | if (pos + ab > dataTemp.Count)
58 | {
59 | //invaild data, not enough to copy.
60 | blockBuilder.Clear();
61 | break;
62 | }
63 | for (byte i = 0; i < ab; i++)
64 | {
65 | blockBuilder.Add(dataTemp[pos++]);
66 | }
67 | }
68 | else if (ab >= 0x80 && ab <= 0xbf)
69 | {
70 | temps.Clear();
71 | temps.Add(0);
72 | temps.Add(0);
73 | temps.Add((byte)(ab & 0x3f));
74 | if (pos < dataTemp.Count)
75 | {
76 | temps.Add(dataTemp[pos++]);
77 | }
78 | else
79 | {
80 | temps.Add(0);
81 | }
82 |
83 | uint b = BytesToUint(temps.ToArray());
84 | uint dist = b >> 3;
85 | int uncompressedPos = blockBuilder.Count - ((int)dist);
86 | if (uncompressedPos >= 0 && dist != 0)
87 | {
88 | uint len = (b & 0x07) + 3;
89 | for (int i = 0; i < len; i++)
90 | {
91 | blockBuilder.Add(blockBuilder[uncompressedPos + i]);
92 | }
93 | }
94 | else
95 | {
96 | //invaild data, distance larger then uncommpressed stream length or equal to 0.
97 | blockBuilder.Clear();
98 | break;
99 | }
100 | }
101 | else if (ab >= 0xc0 && ab <= 0xff)
102 | {
103 | blockBuilder.Add(0x20);
104 | blockBuilder.Add((byte)(ab ^ 0x80));
105 | }
106 | }
107 | return blockBuilder.ToArray();
108 | }
109 |
110 | private static uint BytesToUint(byte[] bytes)
111 | {
112 | return (uint)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/ExtendMethod.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace Roler.Toolkit.File.Mobi
6 | {
7 | internal static class ExtendMethod
8 | {
9 | public static bool TryReadByte(this Stream stream, out byte outResult)
10 | {
11 | byte[] buffer = new byte[1];
12 | if (stream.Read(buffer, 0, 1) == 1)
13 | {
14 | outResult = buffer[0];
15 | return true;
16 | }
17 | outResult = 0;
18 | return false;
19 | }
20 |
21 | public static bool TryReadBytes(this Stream stream, int length, out byte[] outResult)
22 | {
23 | byte[] buffer = new byte[length];
24 | if (stream.Read(buffer, 0, length) == length)
25 | {
26 | outResult = buffer;
27 | return true;
28 | }
29 | outResult = null;
30 | return false;
31 | }
32 |
33 | public static bool TryReadUshort(this Stream stream, out ushort outResult)
34 | {
35 | byte[] buffer = new byte[2];
36 | if (stream.Read(buffer, 0, 2) == 2)
37 | {
38 | outResult = buffer.ToUInt16();
39 | return true;
40 | }
41 | outResult = 0;
42 | return false;
43 | }
44 |
45 | public static bool TryReadUint(this Stream stream, out uint outResult)
46 | {
47 | byte[] buffer = new byte[4];
48 | if (stream.Read(buffer, 0, 4) == 4)
49 | {
50 | outResult = buffer.ToUInt32();
51 | return true;
52 | }
53 | outResult = 0;
54 | return false;
55 | }
56 |
57 | public static bool TryReadString(this Stream stream, int length, out string outResult)
58 | {
59 | byte[] buffer = new byte[length];
60 | if (stream.Read(buffer, 0, length) == length)
61 | {
62 | outResult = System.Text.Encoding.UTF8.GetString(buffer);
63 | return true;
64 | }
65 | outResult = null;
66 | return false;
67 | }
68 |
69 | public static void Skip(this Stream stream)
70 | {
71 | int b;
72 | do
73 | {
74 | b = stream.ReadByte();
75 | } while (b == 0);
76 | if (b > 0)
77 | {
78 | stream.Seek(stream.Position - 1, SeekOrigin.Begin);
79 | }
80 | }
81 |
82 | public static bool CheckStart(this Stream stream, int length, string value)
83 | {
84 | if (stream.TryReadString(length, out string start))
85 | {
86 | return string.Equals(start, value, StringComparison.OrdinalIgnoreCase);
87 | }
88 | return false;
89 | }
90 |
91 | public static ushort ToUInt16(this byte[] bytes)
92 | {
93 | if (bytes.Length == 0)
94 | {
95 | throw new ArgumentException("bytes can not empty.");
96 | }
97 |
98 | var length = Math.Min(2, bytes.Length);
99 | ushort result = 0;
100 | for (int i = 0; i < length; i++)
101 | {
102 | result |= (ushort)(bytes[i] << (length - 1 - i) * 8);
103 | }
104 | return result;
105 | }
106 |
107 | public static uint ToUInt32(this byte[] bytes)
108 | {
109 | if (bytes.Length == 0)
110 | {
111 | throw new ArgumentException("bytes can not empty.");
112 | }
113 |
114 | var length = Math.Min(4, bytes.Length);
115 | uint result = 0;
116 | for (int i = 0; i < length; i++)
117 | {
118 | result |= (uint)bytes[i] << ((length - 1 - i) * 8);
119 | }
120 | return result;
121 | }
122 |
123 | public static ulong ToUInt64(this byte[] bytes)
124 | {
125 | if (bytes.Length == 0)
126 | {
127 | throw new ArgumentException("bytes can not empty.");
128 | }
129 |
130 | var length = Math.Min(8, bytes.Length);
131 | ulong result = 0;
132 | for (int i = 0; i < length; i++)
133 | {
134 | result |= (ulong)bytes[i] << ((length - 1 - i) * 8);
135 | }
136 | return result;
137 | }
138 |
139 | public static bool RangeEqual(this byte[] bytes, int start, int length, byte[] second)
140 | {
141 | if (second is null)
142 | {
143 | throw new ArgumentNullException(nameof(second));
144 | }
145 |
146 | return bytes.Skip(start).Take(length).SequenceEqual(second);
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/FileToolkitSample.WPF.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}
8 | WinExe
9 | FileToolkitSample.WPF
10 | FileToolkitSample.WPF
11 | v4.8
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 |
20 | AnyCPU
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 4.0
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | MSBuild:Compile
57 | Designer
58 |
59 |
60 | MSBuild:Compile
61 | Designer
62 |
63 |
64 | App.xaml
65 | Code
66 |
67 |
68 | MainWindow.xaml
69 | Code
70 |
71 |
72 |
73 |
74 | Code
75 |
76 |
77 | True
78 | True
79 | Resources.resx
80 |
81 |
82 | True
83 | Settings.settings
84 | True
85 |
86 |
87 | ResXFileCodeGenerator
88 | Resources.Designer.cs
89 |
90 |
91 | SettingsSingleFileGenerator
92 | Settings.Designer.cs
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | {c67b8f9f-1677-4ff2-bb7b-8cff9ce3b7b6}
101 | Roler.Toolkit.File.Epub
102 |
103 |
104 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}
105 | Roler.Toolkit.File.Mobi
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/RecordEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi.Engine
5 | {
6 | internal static class RecordEngine
7 | {
8 | #region Const String
9 |
10 | public const string Identifier_Flis = "FLIS";
11 | public const string Identifier_Fcis = "FCIS";
12 |
13 | #endregion
14 |
15 | public static bool TryReadFlisRecord(Stream stream, long offset, out FlisRecord flisRecord)
16 | {
17 | bool result = false;
18 | flisRecord = null;
19 | stream.Seek(offset, SeekOrigin.Begin);
20 | if (stream.CheckStart(4, Identifier_Flis))
21 | {
22 | stream.Seek(offset, SeekOrigin.Begin);
23 | flisRecord = new FlisRecord();
24 | if (stream.TryReadString(4, out string identifier))
25 | {
26 | flisRecord.Identifier = identifier;
27 | }
28 | if (stream.TryReadUint(out uint value0))
29 | {
30 | flisRecord.Value0 = value0;
31 | }
32 | if (stream.TryReadUshort(out ushort value1))
33 | {
34 | flisRecord.Value1 = value1;
35 | }
36 | if (stream.TryReadUshort(out ushort value2))
37 | {
38 | flisRecord.Value2 = value2;
39 | }
40 | if (stream.TryReadUint(out uint value3))
41 | {
42 | flisRecord.Value3 = value3;
43 | }
44 | if (stream.TryReadUint(out uint value4))
45 | {
46 | flisRecord.Value4 = value4;
47 | }
48 | if (stream.TryReadUshort(out ushort value5))
49 | {
50 | flisRecord.Value5 = value5;
51 | }
52 | if (stream.TryReadUshort(out ushort value6))
53 | {
54 | flisRecord.Value6 = value6;
55 | }
56 | if (stream.TryReadUint(out uint value7))
57 | {
58 | flisRecord.Value7 = value7;
59 | }
60 | if (stream.TryReadUint(out uint value8))
61 | {
62 | flisRecord.Value8 = value8;
63 | }
64 | if (stream.TryReadUint(out uint value9))
65 | {
66 | flisRecord.Value9 = value9;
67 | }
68 | result = true;
69 | }
70 |
71 | if (!result)
72 | {
73 | stream.Seek(offset, SeekOrigin.Begin);
74 | }
75 |
76 | return result;
77 | }
78 |
79 | public static bool TryReadFcisRecord(Stream stream, long offset, out FcisRecord fcisRecord)
80 | {
81 | bool result = false;
82 | fcisRecord = null;
83 | stream.Seek(offset, SeekOrigin.Begin);
84 | if (stream.CheckStart(4, Identifier_Fcis))
85 | {
86 | stream.Seek(offset, SeekOrigin.Begin);
87 | fcisRecord = new FcisRecord();
88 | if (stream.TryReadString(4, out string identifier))
89 | {
90 | fcisRecord.Identifier = identifier;
91 | }
92 | if (stream.TryReadUint(out uint value0))
93 | {
94 | fcisRecord.Value0 = value0;
95 | }
96 | if (stream.TryReadUint(out uint value1))
97 | {
98 | fcisRecord.Value1 = value1;
99 | }
100 | if (stream.TryReadUint(out uint value2))
101 | {
102 | fcisRecord.Value2 = value2;
103 | }
104 | if (stream.TryReadUint(out uint value3))
105 | {
106 | fcisRecord.Value3 = value3;
107 | }
108 | if (stream.TryReadUint(out uint value4))
109 | {
110 | fcisRecord.Value4 = value4;
111 | }
112 | if (stream.TryReadUint(out uint value5))
113 | {
114 | fcisRecord.Value5 = value5;
115 | }
116 | if (stream.TryReadUint(out uint value6))
117 | {
118 | fcisRecord.Value6 = value6;
119 | }
120 | if (stream.TryReadUint(out uint value7))
121 | {
122 | fcisRecord.Value7 = value7;
123 | }
124 | if (stream.TryReadUshort(out ushort value8))
125 | {
126 | fcisRecord.Value8 = value8;
127 | }
128 | if (stream.TryReadUshort(out ushort value9))
129 | {
130 | fcisRecord.Value9 = value9;
131 | }
132 | if (stream.TryReadUint(out uint value10))
133 | {
134 | fcisRecord.Value10 = value10;
135 | }
136 | result = true;
137 | }
138 |
139 | if (!result)
140 | {
141 | stream.Seek(offset, SeekOrigin.Begin);
142 | }
143 |
144 | return result;
145 | }
146 |
147 | public static void Write(IndxHeader file, Stream stream)
148 | {
149 | }
150 |
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Engine/NcxEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 | using Roler.Toolkit.File.Epub.Define;
7 | using Roler.Toolkit.File.Epub.Entity;
8 |
9 | namespace Roler.Toolkit.File.Epub.Engine
10 | {
11 | internal static class NcxEngine
12 | {
13 | #region Define
14 |
15 | public const string ELEMENT_NCX = "ncx";
16 | public const string ELEMENT_NAVMAP = "navMap";
17 | public const string ELEMENT_NAVINFO = "navInfo";
18 | public const string ELEMENT_TEXT = "text";
19 | public const string ELEMENT_NAVPOINT = "navPoint";
20 | public const string ELEMENT_NAVLABEL = "navLabel";
21 | public const string ELEMENT_CONTENT = "content";
22 |
23 | public const string ATTRIBUTE_VERSION = "version";
24 | public const string ATTRIBUTE_ID = "id";
25 | public const string ATTRIBUTE_PLAYORDER = "playOrder";
26 | public const string ATTRIBUTE_SRC = "src";
27 |
28 | #endregion
29 |
30 | public static Ncx Read(Stream stream)
31 | {
32 | Ncx result = null;
33 | using (var streamReader = new StreamReader(stream))
34 | {
35 | string xmlStr = streamReader.ReadToEnd();
36 | var document = XElement.Parse(xmlStr.FixXml());
37 |
38 | var xNamespace = document.GetDefaultNamespace();
39 | result = ParseNcx(document) ?? throw new InvalidDataException("invalid data of ncx file: ncx");
40 | }
41 | return result;
42 | }
43 |
44 | public static void Write(Ncx file, Stream stream)
45 | {
46 | }
47 |
48 | private static Ncx ParseNcx(XElement element)
49 | {
50 | Ncx result = null;
51 |
52 | if (element != null && element.Name.LocalName == ELEMENT_NCX)
53 | {
54 | var xNamespace = element.Name.Namespace;
55 |
56 | result = new Ncx
57 | {
58 | Version = element.Attribute(ATTRIBUTE_VERSION)?.Value,
59 | Language = element.Attribute(Const.ATTRIBUTE_LANGUAGE)?.Value,
60 | NavMap = ParseNavMap(element.Element(xNamespace + ELEMENT_NAVMAP)),
61 | };
62 | }
63 |
64 | return result;
65 | }
66 |
67 | private static NavMap ParseNavMap(XElement element)
68 | {
69 | NavMap result = null;
70 |
71 | if (element != null && element.Name.LocalName == ELEMENT_NAVMAP)
72 | {
73 | var xNamespace = element.Name.Namespace;
74 | result = new NavMap();
75 | var infoText = element.Element(xNamespace + ELEMENT_NAVINFO)?.Element(xNamespace + ELEMENT_TEXT).Value;
76 | if (!String.IsNullOrWhiteSpace(infoText))
77 | {
78 | result.NavInfo = new NavInfo { Text = infoText };
79 | }
80 | result.NavPoints = ParseNavPointList(element.Elements(xNamespace + ELEMENT_NAVPOINT), xNamespace);
81 | }
82 |
83 | return result;
84 | }
85 |
86 | private static IList ParseNavPointList(IEnumerable elements, XNamespace xNamespace)
87 | {
88 | IList result = null;
89 | if (elements != null && elements.Any())
90 | {
91 | var navPoints = new List();
92 | var data = from element in elements
93 | select new NavPoint
94 | {
95 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
96 | PlayOrder = element.Attribute(ATTRIBUTE_PLAYORDER)?.Value,
97 | Label = ParseNavPointLabel(element.Element(xNamespace + ELEMENT_NAVLABEL)),
98 | Content = ParseNavPointContent(element.Element(xNamespace + ELEMENT_CONTENT)),
99 | Children = ParseNavPointList(element.Elements(xNamespace + ELEMENT_NAVPOINT), xNamespace),
100 | };
101 | navPoints.AddRange(data);
102 | result = navPoints;
103 | }
104 | return result;
105 | }
106 |
107 | private static NavPointLabel ParseNavPointLabel(XElement element)
108 | {
109 | NavPointLabel result = null;
110 | if (element != null && element.Name.LocalName == ELEMENT_NAVLABEL)
111 | {
112 | var xNamespace = element.Name.Namespace;
113 | var text = element.Element(xNamespace + ELEMENT_TEXT).Value;
114 | if (!String.IsNullOrWhiteSpace(text))
115 | {
116 | result = new NavPointLabel { Text = text };
117 | }
118 | }
119 | return result;
120 | }
121 |
122 | private static NavPointContent ParseNavPointContent(XElement element)
123 | {
124 | NavPointContent result = null;
125 | if (element != null && element.Name.LocalName == ELEMENT_CONTENT)
126 | {
127 | result = new NavPointContent
128 | {
129 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
130 | Source = element.Attribute(ATTRIBUTE_SRC)?.Value,
131 | };
132 | }
133 | return result;
134 | }
135 |
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.WPF/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Engine/NavEngine.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Xml.Linq;
4 | using Roler.Toolkit.File.Epub.Define;
5 | using Roler.Toolkit.File.Epub.Entity;
6 |
7 | namespace Roler.Toolkit.File.Epub.Engine
8 | {
9 | internal static class NavEngine
10 | {
11 | #region Define
12 |
13 | public const string ELEMENT_NAV = "nav";
14 | public const string ELEMENT_H1 = "h1";
15 | public const string ELEMENT_H2 = "h2";
16 | public const string ELEMENT_H3 = "h3";
17 | public const string ELEMENT_H4 = "h4";
18 | public const string ELEMENT_H5 = "h5";
19 | public const string ELEMENT_H6 = "h6";
20 | public const string ELEMENT_OL = "ol";
21 | public const string ELEMENT_LI = "li";
22 | public const string ELEMENT_A = "a";
23 | public const string ELEMENT_SPAN = "span";
24 |
25 | public static readonly XName ATTRIBUTE_TYPE = Const.EPUB_NAMESPACE + "type";
26 | public const string ATTRIBUTE_ID = "id";
27 | public const string ATTRIBUTE_HIDDEN = "hidden";
28 | public const string ATTRIBUTE_HREF = "href";
29 | public const string ATTRIBUTE_TITLE = "title";
30 |
31 | private static readonly IReadOnlyList NAV_HEAD_LIST = new List { ELEMENT_H1, ELEMENT_H2, ELEMENT_H3, ELEMENT_H4, ELEMENT_H5, ELEMENT_H6 };
32 |
33 | #endregion
34 |
35 | public static Nav Read(Stream stream)
36 | {
37 | Nav result = null;
38 | using (var streamReader = new StreamReader(stream))
39 | {
40 | string xmlStr = streamReader.ReadToEnd();
41 | var document = XElement.Parse(xmlStr.FixXml());
42 |
43 | var xNamespace = document.GetDefaultNamespace();
44 | result = ParseNav(document) ?? throw new InvalidDataException("invalid data of nav file: nav");
45 | }
46 | return result;
47 | }
48 |
49 | public static void Write(Nav file, Stream stream)
50 | {
51 | }
52 |
53 | private static Nav ParseNav(XElement element)
54 | {
55 | Nav result = null;
56 |
57 | if (element != null)
58 | {
59 | result = new Nav();
60 |
61 | var xNamespace = element.GetDefaultNamespace();
62 | foreach (var nav in element.Descendants(xNamespace + ELEMENT_NAV))
63 | {
64 | var navElement = ParseNavElement(nav);
65 | if (navElement != null)
66 | {
67 | result.NavElements.Add(navElement);
68 | }
69 | }
70 | }
71 |
72 | return result;
73 | }
74 |
75 | private static NavElement ParseNavElement(XElement element)
76 | {
77 | NavElement result = null;
78 |
79 | if (element != null && element.Name.LocalName == ELEMENT_NAV && element.Attribute(ATTRIBUTE_TYPE) != null)
80 | {
81 | var xNamespace = element.GetDefaultNamespace();
82 |
83 | result = new NavElement
84 | {
85 | Type = element.Attribute(ATTRIBUTE_TYPE)?.Value,
86 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
87 | IsHidden = element.Attribute(ATTRIBUTE_HIDDEN) != null,
88 | Ol = ParseNavOl(element.Element(xNamespace + ELEMENT_OL)),
89 | };
90 |
91 | foreach (var head in NAV_HEAD_LIST)
92 | {
93 | var headELement = element.Element(xNamespace + head);
94 | if (headELement != null)
95 | {
96 | result.NavHead = new NavHead
97 | {
98 | Name = head,
99 | Value = headELement.Value,
100 | };
101 | break;
102 | }
103 | }
104 |
105 | }
106 |
107 | return result;
108 | }
109 |
110 | private static NavOl ParseNavOl(XElement element)
111 | {
112 | NavOl result = null;
113 |
114 | if (element != null && element.Name.LocalName == ELEMENT_OL)
115 | {
116 | var xNamespace = element.GetDefaultNamespace();
117 |
118 | result = new NavOl
119 | {
120 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
121 | IsHidden = element.Attribute(ATTRIBUTE_HIDDEN) != null,
122 | };
123 |
124 | foreach (var liElement in element.Elements(xNamespace + ELEMENT_LI))
125 | {
126 | var navLi = ParseNavLi(liElement);
127 | if (navLi != null)
128 | {
129 | result.Items.Add(navLi);
130 | }
131 | }
132 | }
133 |
134 | return result;
135 | }
136 |
137 | private static NavLi ParseNavLi(XElement element)
138 | {
139 | NavLi result = null;
140 |
141 | if (element != null && element.Name.LocalName == ELEMENT_LI)
142 | {
143 | var xNamespace = element.GetDefaultNamespace();
144 |
145 | result = new NavLi
146 | {
147 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
148 | IsHidden = element.Attribute(ATTRIBUTE_HIDDEN) != null,
149 | Ol = ParseNavOl(element.Element(xNamespace + ELEMENT_OL)),
150 | };
151 |
152 | var aElement = element.Element(xNamespace + ELEMENT_A);
153 | if (aElement != null)
154 | {
155 | result.A = new NavA
156 | {
157 | Href = aElement.Attribute(ATTRIBUTE_HREF)?.Value,
158 | Title = aElement.Attribute(ATTRIBUTE_TITLE)?.Value,
159 | Value = aElement.Value,
160 | };
161 | }
162 | var spanElement = element.Element(xNamespace + ELEMENT_SPAN);
163 | if (spanElement != null)
164 | {
165 | result.Span = new NavSpan
166 | {
167 | Value = spanElement.Value,
168 | };
169 | }
170 | }
171 |
172 | return result;
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Entity/MobiHeader/MobiHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Roler.Toolkit.File.Mobi.Entity
4 | {
5 | public class MobiHeader
6 | {
7 | ///
8 | /// Gets or sets the identifier, always 'MOBI'.
9 | ///
10 | public string Identifier { get; set; }
11 |
12 | ///
13 | /// Gets or sets the length of the MOBI header, including the previous 4 bytes.
14 | ///
15 | public uint Length { get; set; }
16 |
17 | public MobiType MobiType { get; set; }
18 |
19 | public TextEncoding TextEncoding { get; set; }
20 |
21 | ///
22 | /// Some kind of unique ID number.
23 | ///
24 | public uint UniqueId { get; set; }
25 |
26 | ///
27 | /// Gets or sets the version of the Mobipocket format used in this file.
28 | ///
29 | public uint FileVersion { get; set; }
30 |
31 | ///
32 | /// Gets or sets the section number of orthographic meta index. 0xFFFFFFFF if index is not available.
33 | ///
34 | public uint OrtographicIndex { get; set; }
35 |
36 | ///
37 | /// Gets or sets the section number of inflection meta index. 0xFFFFFFFF if index is not available.
38 | ///
39 | public uint InflectionIndex { get; set; }
40 |
41 | public uint IndexNames { get; set; }
42 |
43 | public uint IndexKeys { get; set; }
44 |
45 | ///
46 | /// Gets or sets the section number of extra 0 meta index. 0xFFFFFFFF if index is not available.
47 | ///
48 | public uint ExtraIndex0 { get; set; }
49 |
50 | ///
51 | /// Gets or sets the section number of extra 1 meta index. 0xFFFFFFFF if index is not available.
52 | ///
53 | public uint ExtraIndex1 { get; set; }
54 |
55 | ///
56 | /// Gets or sets the section number of extra 2 meta index. 0xFFFFFFFF if index is not available.
57 | ///
58 | public uint ExtraIndex2 { get; set; }
59 |
60 | ///
61 | /// Gets or sets the section number of extra 3 meta index. 0xFFFFFFFF if index is not available.
62 | ///
63 | public uint ExtraIndex3 { get; set; }
64 |
65 | ///
66 | /// Gets or sets the section number of extra 4 meta index. 0xFFFFFFFF if index is not available.
67 | ///
68 | public uint ExtraIndex4 { get; set; }
69 |
70 | ///
71 | /// Gets or sets the section number of extra 5 meta index. 0xFFFFFFFF if index is not available.
72 | ///
73 | public uint ExtraIndex5 { get; set; }
74 |
75 | ///
76 | /// Gets or sets the first record number (starting with 0) that's not the book's text.
77 | ///
78 | public uint FirstNonBookIndex { get; set; }
79 |
80 | ///
81 | /// Gets or sets the offset in record 0 (not from start of file) of the full name of the book.
82 | ///
83 | public uint FullNameOffset { get; set; }
84 |
85 | ///
86 | /// Gets or sets the length in bytes of the full name of the book.
87 | ///
88 | public uint FullNameLength { get; set; }
89 |
90 | ///
91 | /// Gets or sets the book locale code. Low byte is main language 09= English, next byte is dialect, 08 = British, 04 = US. Thus US English is 1033, UK English is 2057.
92 | ///
93 | public uint Locale { get; set; }
94 |
95 | ///
96 | /// Gets or sets the input language for a dictionary.
97 | ///
98 | public string InputLanguage { get; set; }
99 |
100 | ///
101 | /// Gets or sets the output language for a dictionary.
102 | ///
103 | public string OutputLanguage { get; set; }
104 |
105 | ///
106 | /// Gets or sets the minimum mobipocket version support needed to read this file.
107 | ///
108 | public uint MinVersion { get; set; }
109 |
110 | ///
111 | /// Gets or sets the first record number (starting with 0) that contains an image. Image records should be sequential.
112 | ///
113 | public uint FirstImageIndex { get; set; }
114 |
115 | ///
116 | /// Gets or sets the record number of the first huffman compression record.
117 | ///
118 | public uint HuffmanRecordOffset { get; set; }
119 |
120 | ///
121 | /// Gets or sets the number of huffman compression records.
122 | ///
123 | public uint HuffmanRecordCount { get; set; }
124 |
125 | public uint HuffmanTableOffset { get; set; }
126 |
127 | public uint HuffmanTableLength { get; set; }
128 |
129 | public uint EXTHFlags { get; set; }
130 |
131 | ///
132 | /// Gets or sets the offset to DRM key info in DRMed files. 0xFFFFFFFF if no DRM.
133 | ///
134 | public uint DRMOffset { get; set; }
135 |
136 | ///
137 | /// Gets or sets the number of entries in DRM info. 0xFFFFFFFF if no DRM.
138 | ///
139 | public uint DRMCount { get; set; }
140 |
141 | ///
142 | /// Gets or sets the number of bytes in DRM info.
143 | ///
144 | public uint DRMSize { get; set; }
145 |
146 | ///
147 | /// Gets or sets some flags concerning the DRM info.
148 | ///
149 | public uint DRMFlags { get; set; }
150 |
151 | ///
152 | /// Gets or sets the number of first text record. Normally 1.
153 | ///
154 | public ushort FirstContentRecordOffset { get; set; }
155 |
156 | ///
157 | /// Gets or sets the number of last image record or number of last text record if it contains no images. Includes Image, DATP, HUFF, DRM. .
158 | ///
159 | public ushort LastContentRecordOffset { get; set; }
160 |
161 | public uint FCISRecordOffset { get; set; }
162 |
163 | public uint FLISRecordOffset { get; set; }
164 |
165 | public uint ExtraRecordDataFlags { get; set; }
166 |
167 | ///
168 | /// Gets or sets the record number of the first INDX record created from an ncx file.
169 | ///
170 | public uint INDXRecordOffset { get; set; }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Compression/HuffCdicCompression.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace Roler.Toolkit.File.Mobi.Compression
7 | {
8 | ///
9 | /// HUFF/CDIC compression
10 | ///
11 | internal class HuffCdicCompression : ICompression
12 | {
13 | public readonly static byte[] HuffBytesStart = { 0x48, 0x55, 0x46, 0x46, 0x00, 0x00, 0x00, 0x18 };
14 | public readonly static byte[] CdicBytesStart = { 0x43, 0x44, 0x49, 0x43, 0x00, 0x00, 0x00, 0x10 };
15 |
16 | private readonly List _huff;
17 | private readonly List _cidcList;
18 | private readonly IList> _huffDict1 = new List>();
19 | private readonly IList _minCodeList = new List();
20 | private readonly IList _maxCodeList = new List();
21 | private readonly IList, int>> _dictionary = new List, int>>();
22 |
23 | public IReadOnlyList Huff => this._huff;
24 | public IReadOnlyList CdicList => this._cidcList;
25 | public uint ExtraFlags { get; set; }
26 |
27 | public HuffCdicCompression()
28 | {
29 | }
30 |
31 | public HuffCdicCompression(byte[] huff, IList cdicList)
32 | {
33 | if (huff is null)
34 | {
35 | throw new ArgumentNullException(nameof(huff));
36 | }
37 |
38 | if (cdicList is null)
39 | {
40 | throw new ArgumentNullException(nameof(cdicList));
41 | }
42 |
43 | this._huff = new List(huff);
44 | this._cidcList = new List(cdicList);
45 |
46 | this.InitMember();
47 | }
48 |
49 | public byte[] Compress(byte[] bytes)
50 | {
51 | if (bytes is null)
52 | {
53 | throw new ArgumentNullException(nameof(bytes));
54 | }
55 |
56 | throw new NotImplementedException();
57 | }
58 |
59 | public byte[] Decompress(byte[] bytes)
60 | {
61 | if (bytes is null)
62 | {
63 | throw new ArgumentNullException(nameof(bytes));
64 | }
65 | return this.Unpack(bytes);
66 | }
67 |
68 | private void InitMember()
69 | {
70 | this.LoadHuff();
71 |
72 | this._dictionary.Clear();
73 | foreach (var cdicBytes in this._cidcList)
74 | {
75 | this.LoadOneCdic(cdicBytes);
76 | }
77 | }
78 |
79 | private void LoadHuff()
80 | {
81 | if (this._huff.GetRange(0, 8).SequenceEqual(HuffBytesStart))
82 | {
83 | uint off1 = this._huff.GetRange(8, 4).ToArray().ToUInt32();
84 | uint off2 = this._huff.GetRange(12, 4).ToArray().ToUInt32();
85 |
86 | this._huffDict1.Clear();
87 | for (int i = 0; i < 256; i++)
88 | {
89 | uint v = this._huff.GetRange((int)(off1 + (i * 4)), 4).ToArray().ToUInt32();
90 | var codeLength = (int)(v & 0x1f);
91 | var term = (int)(v & 0x80);
92 | uint maxCode = v >> 8;
93 |
94 | if (codeLength == 0)
95 | {
96 | throw new InvalidDataException("invalid data: Huff");
97 | }
98 | else
99 | {
100 | maxCode = ((maxCode + 1) << (32 - codeLength)) - 1;
101 | }
102 | this._huffDict1.Add(new Tuple(codeLength, term, maxCode));
103 | }
104 |
105 | this._minCodeList.Clear();
106 | this._maxCodeList.Clear();
107 |
108 | this._minCodeList.Add(uint.MinValue);
109 | this._maxCodeList.Add(uint.MaxValue);
110 | for (int i = 0; i < 64; i++)
111 | {
112 | var v = this._huff.GetRange((int)(off2 + (i * 4)), 4).ToArray().ToUInt32();
113 | var step = 32 - i / 2 - 1;
114 | if (i % 2 == 0)
115 | {
116 | this._minCodeList.Add(v << step);
117 | }
118 | else
119 | {
120 | this._maxCodeList.Add(((v + 1) << step) - 1);
121 | }
122 | }
123 | }
124 | }
125 |
126 | private void LoadOneCdic(byte[] cdicBytes)
127 | {
128 | if (cdicBytes is null)
129 | {
130 | throw new ArgumentNullException(nameof(cdicBytes));
131 | }
132 | if (cdicBytes.RangeEqual(0, 8, CdicBytesStart))
133 | {
134 | var phrases = cdicBytes.Skip(8).Take(4).ToArray().ToUInt32();
135 | var bits = cdicBytes.Skip(12).Take(4).ToArray().ToUInt32();
136 |
137 | var n = Math.Min(1 << (int)bits, phrases - this._dictionary.Count);
138 | for (int i = 0; i < n; i++)
139 | {
140 | var off = cdicBytes.Skip(16 + (i * 2)).Take(2).ToArray().ToUInt16();
141 | var blen = cdicBytes.Skip(16 + off).Take(2).ToArray().ToUInt16();
142 | var length = 18 + off + (blen & 0x7fff);
143 | var sliceByteList = new List();
144 | for (int j = 18 + off; j < length; j++)
145 | {
146 | sliceByteList.Add(cdicBytes[j]);
147 | }
148 | this._dictionary.Add(new Tuple, int>(sliceByteList, blen & 0x8000));
149 | }
150 | }
151 | }
152 |
153 | private byte[] Unpack(byte[] bytes)
154 | {
155 | var bitsLeft = bytes.Length * 8;
156 | var data = new List(bytes);
157 | data.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
158 | var pos = 0;
159 | var x = data.GetRange(pos, 8).ToArray().ToUInt64();
160 | var n = 32;
161 | var result = new List();
162 |
163 | while (true)
164 | {
165 | if (n <= 0)
166 | {
167 | pos += 4;
168 | x = data.GetRange(pos, 8).ToArray().ToUInt64();
169 | n += 32;
170 | }
171 |
172 | var code = (uint)((x >> n) & 0xFFFFFFFF);
173 | var tuple = this._huffDict1[(int)(code >> 24)];
174 | var codeLength = tuple.Item1;
175 | var maxCode = tuple.Item3;
176 | if (tuple.Item2 <= 0)
177 | {
178 | while (code < this._minCodeList[codeLength])
179 | {
180 | codeLength++;
181 | }
182 |
183 | maxCode = this._maxCodeList[codeLength];
184 | }
185 |
186 | n -= codeLength;
187 | bitsLeft -= codeLength;
188 | if (bitsLeft < 0)
189 | break;
190 |
191 | var r = (int)((maxCode - code) >> (32 - codeLength));
192 | var tuple2 = this._dictionary[r];
193 | var sliceList = tuple2.Item1;
194 | if (tuple2.Item2 <= 0)
195 | {
196 | this._dictionary.RemoveAt(r);
197 | this._dictionary.Insert(r, null);
198 | sliceList = this.Unpack(sliceList.ToArray());
199 | this._dictionary.RemoveAt(r);
200 | this._dictionary.Insert(r, new Tuple, int>(sliceList, 1));
201 | }
202 |
203 | result.AddRange(sliceList);
204 | }
205 |
206 | return result.ToArray();
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/Engine/MobiHeaderEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Roler.Toolkit.File.Mobi.Entity;
3 |
4 | namespace Roler.Toolkit.File.Mobi.Engine
5 | {
6 | internal static class MobiHeaderEngine
7 | {
8 | #region Const String
9 |
10 | public const string Identifier = "MOBI";
11 | public const uint UnavailableIndex = 0xFFFFFFFF;
12 |
13 | #endregion
14 |
15 | public static bool TryRead(Stream stream, long offset, out MobiHeader mobiHeader)
16 | {
17 | bool result = false;
18 | mobiHeader = null;
19 | stream.Seek(offset, SeekOrigin.Begin);
20 | if (stream.CheckStart(4, Identifier))
21 | {
22 | mobiHeader = Read(stream, offset);
23 | result = true;
24 | }
25 |
26 | if (!result)
27 | {
28 | stream.Seek(offset, SeekOrigin.Begin);
29 | }
30 |
31 | return result;
32 | }
33 |
34 | public static MobiHeader Read(Stream stream, long offset)
35 | {
36 | MobiHeader result = new MobiHeader();
37 | stream.Seek(offset, SeekOrigin.Begin);
38 | if (stream.TryReadString(4, out string identifier))
39 | {
40 | result.Identifier = identifier;
41 | }
42 | if (stream.TryReadUint(out uint length))
43 | {
44 | result.Length = length;
45 | }
46 | if (stream.TryReadUint(out uint mobiType))
47 | {
48 | result.MobiType = (MobiType)mobiType;
49 | }
50 | if (stream.TryReadUint(out uint textEncoding))
51 | {
52 | result.TextEncoding = (TextEncoding)textEncoding;
53 | }
54 | if (stream.TryReadUint(out uint uniqueId))
55 | {
56 | result.UniqueId = uniqueId;
57 | }
58 | if (stream.TryReadUint(out uint fileVersion))
59 | {
60 | result.FileVersion = fileVersion;
61 | }
62 | if (stream.TryReadUint(out uint ortographicIndex))
63 | {
64 | result.OrtographicIndex = ortographicIndex;
65 | }
66 | if (stream.TryReadUint(out uint inflectionIndex))
67 | {
68 | result.InflectionIndex = inflectionIndex;
69 | }
70 | if (stream.TryReadUint(out uint indexNames))
71 | {
72 | result.IndexNames = indexNames;
73 | }
74 | if (stream.TryReadUint(out uint indexKeys))
75 | {
76 | result.IndexKeys = indexKeys;
77 | }
78 | if (stream.TryReadUint(out uint extraIndex0))
79 | {
80 | result.ExtraIndex0 = extraIndex0;
81 | }
82 | if (stream.TryReadUint(out uint extraIndex1))
83 | {
84 | result.ExtraIndex1 = extraIndex1;
85 | }
86 | if (stream.TryReadUint(out uint extraIndex2))
87 | {
88 | result.ExtraIndex2 = extraIndex2;
89 | }
90 | if (stream.TryReadUint(out uint extraIndex3))
91 | {
92 | result.ExtraIndex3 = extraIndex3;
93 | }
94 | if (stream.TryReadUint(out uint extraIndex4))
95 | {
96 | result.ExtraIndex4 = extraIndex4;
97 | }
98 | if (stream.TryReadUint(out uint extraIndex5))
99 | {
100 | result.ExtraIndex5 = extraIndex5;
101 | }
102 | if (stream.TryReadUint(out uint firstNonBookIndex))
103 | {
104 | result.FirstNonBookIndex = firstNonBookIndex;
105 | }
106 | if (stream.TryReadUint(out uint fullNameOffset))
107 | {
108 | result.FullNameOffset = fullNameOffset;
109 | }
110 | if (stream.TryReadUint(out uint fullNameLength))
111 | {
112 | result.FullNameLength = fullNameLength;
113 | }
114 | if (stream.TryReadUint(out uint locale))
115 | {
116 | result.Locale = locale;
117 | }
118 | if (stream.TryReadString(4, out string inputLanguage))
119 | {
120 | result.InputLanguage = inputLanguage;
121 | }
122 | if (stream.TryReadString(4, out string outputLanguage))
123 | {
124 | result.OutputLanguage = outputLanguage;
125 | }
126 | if (stream.TryReadUint(out uint minVersion))
127 | {
128 | result.MinVersion = minVersion;
129 | }
130 | if (stream.TryReadUint(out uint firstImageIndex))
131 | {
132 | result.FirstImageIndex = firstImageIndex;
133 | }
134 | if (stream.TryReadUint(out uint huffmanRecordOffset))
135 | {
136 | result.HuffmanRecordOffset = huffmanRecordOffset;
137 | }
138 | if (stream.TryReadUint(out uint huffmanRecordCount))
139 | {
140 | result.HuffmanRecordCount = huffmanRecordCount;
141 | }
142 | if (stream.TryReadUint(out uint huffmanTableOffset))
143 | {
144 | result.HuffmanTableOffset = huffmanTableOffset;
145 | }
146 | if (stream.TryReadUint(out uint huffmanTableLength))
147 | {
148 | result.HuffmanTableLength = huffmanTableLength;
149 | }
150 | if (stream.TryReadUint(out uint exthFlags))
151 | {
152 | result.EXTHFlags = exthFlags;
153 | }
154 |
155 | stream.Seek(32 + 4, SeekOrigin.Current); //skip 32 unknown bytes and 0xFFFFFFFF.
156 |
157 | if (stream.TryReadUint(out uint drmOffset))
158 | {
159 | result.DRMOffset = drmOffset;
160 | }
161 | if (stream.TryReadUint(out uint drmCount))
162 | {
163 | result.DRMCount = drmCount;
164 | }
165 | if (stream.TryReadUint(out uint drmSize))
166 | {
167 | result.DRMSize = drmSize;
168 | }
169 | if (stream.TryReadUint(out uint drmFlags))
170 | {
171 | result.DRMFlags = drmFlags;
172 | }
173 |
174 | stream.Seek(8, SeekOrigin.Current); //skip 8 bytes, Bytes to the end of the MOBI header, including the following if the header length >= 228 (244 from start of record). Use 0x0000000000000000.
175 |
176 | if (stream.TryReadUshort(out ushort firstContentRecordOffset))
177 | {
178 | result.FirstContentRecordOffset = firstContentRecordOffset;
179 | }
180 | if (stream.TryReadUshort(out ushort lastContentRecordOffset))
181 | {
182 | result.LastContentRecordOffset = lastContentRecordOffset;
183 | }
184 |
185 | stream.Seek(4, SeekOrigin.Current); //skip 4 bytes, 0x00000001.
186 |
187 | if (stream.TryReadUint(out uint fcisRecordOffset))
188 | {
189 | result.FCISRecordOffset = fcisRecordOffset;
190 | }
191 |
192 | stream.Seek(4, SeekOrigin.Current); //skip 4 bytes, 0x00000001.
193 |
194 | if (stream.TryReadUint(out uint flisRecordOffset))
195 | {
196 | result.FLISRecordOffset = flisRecordOffset;
197 | }
198 |
199 | stream.Seek(28, SeekOrigin.Current); //skip 0x00000001, 0x0000000000000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
200 |
201 | if (stream.TryReadUint(out uint extraRecordDataFlags))
202 | {
203 | result.ExtraRecordDataFlags = extraRecordDataFlags;
204 | }
205 | if (stream.TryReadUint(out uint iNDXRecordOffset))
206 | {
207 | result.INDXRecordOffset = iNDXRecordOffset;
208 | }
209 |
210 | stream.Seek(offset + length, SeekOrigin.Begin); //skip to end.
211 |
212 | return result;
213 | }
214 |
215 | public static void Write(MobiHeader file, Stream stream)
216 | {
217 | }
218 |
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Samples/FileToolkitSample.UWP/FileToolkitSample.UWP.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x86
7 | {5D986DC5-9B28-486B-A713-3395BDED35A5}
8 | AppContainerExe
9 | Properties
10 | FileToolkitSample.UWP
11 | FileToolkitSample.UWP
12 | en-US
13 | UAP
14 | 10.0.17763.0
15 | 10.0.16299.0
16 | 14
17 | 512
18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
19 | true
20 | FileToolkitSample.UWP_TemporaryKey.pfx
21 |
22 |
23 | true
24 | bin\x86\Debug\
25 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
26 | ;2008
27 | full
28 | x86
29 | false
30 | prompt
31 | true
32 |
33 |
34 | bin\x86\Release\
35 | TRACE;NETFX_CORE;WINDOWS_UWP
36 | true
37 | ;2008
38 | pdbonly
39 | x86
40 | false
41 | prompt
42 | true
43 | true
44 |
45 |
46 | true
47 | bin\ARM\Debug\
48 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
49 | ;2008
50 | full
51 | ARM
52 | false
53 | prompt
54 | true
55 |
56 |
57 | bin\ARM\Release\
58 | TRACE;NETFX_CORE;WINDOWS_UWP
59 | true
60 | ;2008
61 | pdbonly
62 | ARM
63 | false
64 | prompt
65 | true
66 | true
67 |
68 |
69 | true
70 | bin\ARM64\Debug\
71 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
72 | ;2008
73 | full
74 | ARM64
75 | false
76 | prompt
77 | true
78 | true
79 |
80 |
81 | bin\ARM64\Release\
82 | TRACE;NETFX_CORE;WINDOWS_UWP
83 | true
84 | ;2008
85 | pdbonly
86 | ARM64
87 | false
88 | prompt
89 | true
90 | true
91 |
92 |
93 | true
94 | bin\x64\Debug\
95 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
96 | ;2008
97 | full
98 | x64
99 | false
100 | prompt
101 | true
102 |
103 |
104 | bin\x64\Release\
105 | TRACE;NETFX_CORE;WINDOWS_UWP
106 | true
107 | ;2008
108 | pdbonly
109 | x64
110 | false
111 | prompt
112 | true
113 | true
114 |
115 |
116 | PackageReference
117 |
118 |
119 |
120 | App.xaml
121 |
122 |
123 | MainPage.xaml
124 |
125 |
126 |
127 |
128 |
129 | Designer
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | MSBuild:Compile
146 | Designer
147 |
148 |
149 | MSBuild:Compile
150 | Designer
151 |
152 |
153 |
154 |
155 | 6.2.3
156 |
157 |
158 |
159 |
160 | {c67b8f9f-1677-4ff2-bb7b-8cff9ce3b7b6}
161 | Roler.Toolkit.File.Epub
162 |
163 |
164 |
165 | 14.0
166 |
167 |
168 |
175 |
--------------------------------------------------------------------------------
/RolerFileToolkit/RolerFileToolkit.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29409.12
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roler.Toolkit.File.Epub", "Roler.Toolkit.File.Epub\Roler.Toolkit.File.Epub.csproj", "{C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{C329D920-D541-4A0D-857D-6001C468EAA5}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileToolkitSample.UWP", "Samples\FileToolkitSample.UWP\FileToolkitSample.UWP.csproj", "{5D986DC5-9B28-486B-A713-3395BDED35A5}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileToolkitSample.WPF", "Samples\FileToolkitSample.WPF\FileToolkitSample.WPF.csproj", "{09F75429-54D6-4963-83AA-5E1EFA4872F7}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Roler.Toolkit.File.Mobi", "Roler.Toolkit.File.Mobi\Roler.Toolkit.File.Mobi.csproj", "{512EC6EB-7CB3-4372-8954-CF5E578E5AEE}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Debug|ARM = Debug|ARM
20 | Debug|ARM64 = Debug|ARM64
21 | Debug|x64 = Debug|x64
22 | Debug|x86 = Debug|x86
23 | Release|Any CPU = Release|Any CPU
24 | Release|ARM = Release|ARM
25 | Release|ARM64 = Release|ARM64
26 | Release|x64 = Release|x64
27 | Release|x86 = Release|x86
28 | EndGlobalSection
29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
30 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|ARM.ActiveCfg = Debug|Any CPU
33 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|ARM.Build.0 = Debug|Any CPU
34 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|ARM64.ActiveCfg = Debug|Any CPU
35 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|ARM64.Build.0 = Debug|Any CPU
36 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|x64.ActiveCfg = Debug|Any CPU
37 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|x64.Build.0 = Debug|Any CPU
38 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|x86.ActiveCfg = Debug|Any CPU
39 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Debug|x86.Build.0 = Debug|Any CPU
40 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|ARM.ActiveCfg = Release|Any CPU
43 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|ARM.Build.0 = Release|Any CPU
44 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|ARM64.ActiveCfg = Release|Any CPU
45 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|ARM64.Build.0 = Release|Any CPU
46 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|x64.ActiveCfg = Release|Any CPU
47 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|x64.Build.0 = Release|Any CPU
48 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|x86.ActiveCfg = Release|Any CPU
49 | {C67B8F9F-1677-4FF2-BB7B-8CFF9CE3B7B6}.Release|x86.Build.0 = Release|Any CPU
50 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|Any CPU.ActiveCfg = Debug|x86
51 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM.ActiveCfg = Debug|ARM
52 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM.Build.0 = Debug|ARM
53 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM.Deploy.0 = Debug|ARM
54 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM64.ActiveCfg = Debug|ARM64
55 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM64.Build.0 = Debug|ARM64
56 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|ARM64.Deploy.0 = Debug|ARM64
57 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x64.ActiveCfg = Debug|x64
58 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x64.Build.0 = Debug|x64
59 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x64.Deploy.0 = Debug|x64
60 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x86.ActiveCfg = Debug|x86
61 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x86.Build.0 = Debug|x86
62 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Debug|x86.Deploy.0 = Debug|x86
63 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|Any CPU.ActiveCfg = Release|x86
64 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM.ActiveCfg = Release|ARM
65 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM.Build.0 = Release|ARM
66 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM.Deploy.0 = Release|ARM
67 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM64.ActiveCfg = Release|ARM64
68 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM64.Build.0 = Release|ARM64
69 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|ARM64.Deploy.0 = Release|ARM64
70 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x64.ActiveCfg = Release|x64
71 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x64.Build.0 = Release|x64
72 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x64.Deploy.0 = Release|x64
73 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x86.ActiveCfg = Release|x86
74 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x86.Build.0 = Release|x86
75 | {5D986DC5-9B28-486B-A713-3395BDED35A5}.Release|x86.Deploy.0 = Release|x86
76 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
78 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|ARM.ActiveCfg = Debug|Any CPU
79 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|ARM.Build.0 = Debug|Any CPU
80 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|ARM64.ActiveCfg = Debug|Any CPU
81 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|ARM64.Build.0 = Debug|Any CPU
82 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|x64.ActiveCfg = Debug|Any CPU
83 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|x64.Build.0 = Debug|Any CPU
84 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|x86.ActiveCfg = Debug|Any CPU
85 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Debug|x86.Build.0 = Debug|Any CPU
86 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
87 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|Any CPU.Build.0 = Release|Any CPU
88 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|ARM.ActiveCfg = Release|Any CPU
89 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|ARM.Build.0 = Release|Any CPU
90 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|ARM64.ActiveCfg = Release|Any CPU
91 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|ARM64.Build.0 = Release|Any CPU
92 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|x64.ActiveCfg = Release|Any CPU
93 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|x64.Build.0 = Release|Any CPU
94 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|x86.ActiveCfg = Release|Any CPU
95 | {09F75429-54D6-4963-83AA-5E1EFA4872F7}.Release|x86.Build.0 = Release|Any CPU
96 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
97 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
98 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|ARM.ActiveCfg = Debug|Any CPU
99 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|ARM.Build.0 = Debug|Any CPU
100 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|ARM64.ActiveCfg = Debug|Any CPU
101 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|ARM64.Build.0 = Debug|Any CPU
102 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|x64.ActiveCfg = Debug|Any CPU
103 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|x64.Build.0 = Debug|Any CPU
104 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|x86.ActiveCfg = Debug|Any CPU
105 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Debug|x86.Build.0 = Debug|Any CPU
106 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
107 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|Any CPU.Build.0 = Release|Any CPU
108 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|ARM.ActiveCfg = Release|Any CPU
109 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|ARM.Build.0 = Release|Any CPU
110 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|ARM64.ActiveCfg = Release|Any CPU
111 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|ARM64.Build.0 = Release|Any CPU
112 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|x64.ActiveCfg = Release|Any CPU
113 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|x64.Build.0 = Release|Any CPU
114 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|x86.ActiveCfg = Release|Any CPU
115 | {512EC6EB-7CB3-4372-8954-CF5E578E5AEE}.Release|x86.Build.0 = Release|Any CPU
116 | EndGlobalSection
117 | GlobalSection(SolutionProperties) = preSolution
118 | HideSolutionNode = FALSE
119 | EndGlobalSection
120 | GlobalSection(NestedProjects) = preSolution
121 | {5D986DC5-9B28-486B-A713-3395BDED35A5} = {C329D920-D541-4A0D-857D-6001C468EAA5}
122 | {09F75429-54D6-4963-83AA-5E1EFA4872F7} = {C329D920-D541-4A0D-857D-6001C468EAA5}
123 | EndGlobalSection
124 | GlobalSection(ExtensibilityGlobals) = postSolution
125 | SolutionGuid = {B93EFA96-6362-4583-85F1-1A8ED5BCA1A4}
126 | EndGlobalSection
127 | EndGlobal
128 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/Engine/OpfEngine.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Xml.Linq;
3 | using Roler.Toolkit.File.Epub.Define;
4 | using Roler.Toolkit.File.Epub.Entity;
5 |
6 | namespace Roler.Toolkit.File.Epub.Engine
7 | {
8 | internal static class OpfEngine
9 | {
10 | #region Define
11 |
12 | public const string NAMESPACE_DC = "http://purl.org/dc/elements/1.1/";
13 |
14 | public const string ELEMENT_PACKAGE = "package";
15 | public const string ELEMENT_METADATA = "metadata";
16 | public const string ELEMENT_META = "meta";
17 | public const string ELEMENT_LINK = "link";
18 | public const string ELEMENT_MANIFEST = "manifest";
19 | public const string ELEMENT_ITEM = "item";
20 | public const string ELEMENT_SPINE = "spine";
21 | public const string ELEMENT_ITEMREF = "itemref";
22 |
23 | public const string DC_ELEMENT_CONTRIBUTOR = "contributor";
24 | public const string DC_ELEMENT_COVERAGE = "coverage";
25 | public const string DC_ELEMENT_CREATOR = "creator";
26 | public const string DC_ELEMENT_DATE = "date";
27 | public const string DC_ELEMENT_DESCRIPTION = "description";
28 | public const string DC_ELEMENT_FORMAT = "format";
29 | public const string DC_ELEMENT_IDENTIFIER = "identifier";
30 | public const string DC_ELEMENT_LANGUAGE = "language";
31 | public const string DC_ELEMENT_PUBLISHER = "publisher";
32 | public const string DC_ELEMENT_RELATION = "relation";
33 | public const string DC_ELEMENT_RIGHTS = "rights";
34 | public const string DC_ELEMENT_SOURCE = "source";
35 | public const string DC_ELEMENT_SUBJECT = "subject";
36 | public const string DC_ELEMENT_TITLE = "title";
37 | public const string DC_ELEMENT_TYPE = "type";
38 |
39 | public const string ATTRIBUTE_VERSION = "version";
40 | public const string ATTRIBUTE_IDENTIFIER = "unique-identifier";
41 | public const string ATTRIBUTE_PREFIX = "prefix";
42 | public const string ATTRIBUTE_DIR = "dir";
43 | public const string ATTRIBUTE_ID = "id";
44 | public const string ATTRIBUTE_SCHEME = "scheme";
45 | public const string ATTRIBUTE_PROPERTY = "property";
46 | public const string ATTRIBUTE_REFINES = "refines";
47 | public const string ATTRIBUTE_NAME = "name";
48 | public const string ATTRIBUTE_CONTENT = "content";
49 | public const string ATTRIBUTE_HREF = "href";
50 | public const string ATTRIBUTE_REL = "rel";
51 | public const string ATTRIBUTE_MEDIATYPE = "media-type";
52 | public const string ATTRIBUTE_FALLBACK = "fallback";
53 | public const string ATTRIBUTE_PROPERTIES = "properties";
54 | public const string ATTRIBUTE_MEDIAOVERLAY = "media-overlay";
55 | public const string ATTRIBUTE_TOC = "toc";
56 | public const string ATTRIBUTE_PAGEPROGRESSIONDIRECTION = "page-progression-direction";
57 | public const string ATTRIBUTE_IDREF = "idref";
58 | public const string ATTRIBUTE_LINEAR = "linear";
59 |
60 | #endregion
61 |
62 | public static Package Read(Stream stream)
63 | {
64 | Package result = null;
65 | using (var streamReader = new StreamReader(stream))
66 | {
67 | string xmlStr = streamReader.ReadToEnd();
68 | var document = XElement.Parse(xmlStr.FixXml());
69 |
70 | var xNamespace = document.GetDefaultNamespace();
71 | result = ParsePackage(document) ?? throw new InvalidDataException("invalid data of opf file: package");
72 |
73 | var metadataElement = document.Element(xNamespace + ELEMENT_METADATA);
74 | var metadata = ParseMetadata(metadataElement);
75 | result.Metadata = metadata ?? throw new InvalidDataException("invalid data of opf file: metadata");
76 |
77 | var manifestElement = document.Element(xNamespace + ELEMENT_MANIFEST);
78 | var manifest = ParseManifest(manifestElement);
79 | result.Manifest = manifest ?? throw new InvalidDataException("invalid data of opf file: manifest");
80 |
81 | var spineElement = document.Element(xNamespace + ELEMENT_SPINE);
82 | var spine = ParseSpine(spineElement);
83 | result.Spine = spine ?? throw new InvalidDataException("invalid data of opf file: spine");
84 | }
85 | return result;
86 | }
87 |
88 | public static void Write(Container file, Stream stream)
89 | {
90 | }
91 |
92 | private static Package ParsePackage(XElement element)
93 | {
94 | Package result = null;
95 |
96 | if (element != null)
97 | {
98 | result = new Package
99 | {
100 | Identifier = element.Attribute(ATTRIBUTE_IDENTIFIER)?.Value,
101 | Prefix = element.Attribute(ATTRIBUTE_PREFIX)?.Value,
102 | Language = element.Attribute(Const.ATTRIBUTE_LANGUAGE)?.Value,
103 | Dir = element.Attribute(ATTRIBUTE_DIR)?.Value,
104 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
105 | };
106 |
107 | if (float.TryParse(element.Attribute(ATTRIBUTE_VERSION)?.Value, out float version))
108 | {
109 | result.Version = version;
110 | }
111 | }
112 |
113 | return result;
114 | }
115 |
116 | private static Metadata ParseMetadata(XElement element)
117 | {
118 | Metadata result = null;
119 |
120 | if (element != null && element.Name.LocalName == ELEMENT_METADATA)
121 | {
122 | result = new Metadata();
123 | foreach (var childElement in element.Elements())
124 | {
125 | if (childElement.Name.Namespace == NAMESPACE_DC)
126 | {
127 | var dcElement = new DcElement
128 | {
129 | Id = childElement.Attribute(ATTRIBUTE_ID)?.Value,
130 | Language = childElement.Attribute(Const.ATTRIBUTE_LANGUAGE)?.Value,
131 | Dir = childElement.Attribute(ATTRIBUTE_DIR)?.Value,
132 | Value = childElement.Value
133 | };
134 | switch (childElement.Name.LocalName)
135 | {
136 | case DC_ELEMENT_CONTRIBUTOR: result.Contributors.Add(dcElement); break;
137 | case DC_ELEMENT_COVERAGE: result.Coverages.Add(dcElement); break;
138 | case DC_ELEMENT_CREATOR: result.Creators.Add(dcElement); break;
139 | case DC_ELEMENT_DATE: result.Date = dcElement; break;
140 | case DC_ELEMENT_DESCRIPTION: result.Descriptions.Add(dcElement); break;
141 | case DC_ELEMENT_FORMAT: result.Formats.Add(dcElement); break;
142 | case DC_ELEMENT_IDENTIFIER: result.Identifiers.Add(dcElement); break;
143 | case DC_ELEMENT_LANGUAGE: result.Languages.Add(dcElement); break;
144 | case DC_ELEMENT_PUBLISHER: result.Publishers.Add(dcElement); break;
145 | case DC_ELEMENT_RELATION: result.Relations.Add(dcElement); break;
146 | case DC_ELEMENT_RIGHTS: result.Rights.Add(dcElement); break;
147 | case DC_ELEMENT_SOURCE: result.Sources.Add(dcElement); break;
148 | case DC_ELEMENT_SUBJECT: result.Subjects.Add(dcElement); break;
149 | case DC_ELEMENT_TITLE: result.Titles.Add(dcElement); break;
150 | case DC_ELEMENT_TYPE: result.Types.Add(dcElement); break;
151 | default: break;
152 | }
153 | }
154 | else
155 | {
156 | switch (childElement.Name.LocalName)
157 | {
158 | case ELEMENT_META:
159 | {
160 | var metaElement = new MetaElement
161 | {
162 | Id = childElement.Attribute(ATTRIBUTE_ID)?.Value,
163 | Language = childElement.Attribute(Const.ATTRIBUTE_LANGUAGE)?.Value,
164 | Dir = childElement.Attribute(ATTRIBUTE_DIR)?.Value,
165 | Scheme = childElement.Attribute(ATTRIBUTE_SCHEME)?.Value,
166 | Property = childElement.Attribute(ATTRIBUTE_PROPERTY)?.Value,
167 | Refines = childElement.Attribute(ATTRIBUTE_REFINES)?.Value,
168 | Value = childElement.Value,
169 | Name = childElement.Attribute(ATTRIBUTE_NAME)?.Value,
170 | Content = childElement.Attribute(ATTRIBUTE_CONTENT)?.Value,
171 | };
172 | result.Metas.Add(metaElement);
173 | }
174 | break;
175 | case ELEMENT_LINK:
176 | {
177 | var linkElement = new LinkElement
178 | {
179 | Href = childElement.Attribute(ATTRIBUTE_HREF)?.Value,
180 | Rel = childElement.Attribute(ATTRIBUTE_REL)?.Value,
181 | Id = childElement.Attribute(ATTRIBUTE_ID)?.Value,
182 | Refines = childElement.Attribute(ATTRIBUTE_REFINES)?.Value,
183 | MediaType = childElement.Attribute(ATTRIBUTE_MEDIATYPE)?.Value,
184 | };
185 | result.Links.Add(linkElement);
186 | }
187 | break;
188 | default: break;
189 | }
190 | }
191 | }
192 | }
193 |
194 | return result;
195 | }
196 |
197 | private static Manifest ParseManifest(XElement element)
198 | {
199 | Manifest result = null;
200 |
201 | if (element != null && element.Name.LocalName == ELEMENT_MANIFEST)
202 | {
203 | result = new Manifest
204 | {
205 | Id = element.Attribute(ATTRIBUTE_ID)?.Value
206 | };
207 | foreach (var childElement in element.Elements())
208 | {
209 | if (childElement.Name.LocalName == ELEMENT_ITEM)
210 | {
211 | var manifestItem = new ManifestItem
212 | {
213 | Id = childElement.Attribute(ATTRIBUTE_ID)?.Value,
214 | Href = childElement.Attribute(ATTRIBUTE_HREF)?.Value,
215 | MediaType = childElement.Attribute(ATTRIBUTE_MEDIATYPE)?.Value,
216 | Fallback = childElement.Attribute(ATTRIBUTE_FALLBACK)?.Value,
217 | Properties = childElement.Attribute(ATTRIBUTE_PROPERTIES)?.Value,
218 | MediaOverlay = childElement.Attribute(ATTRIBUTE_MEDIAOVERLAY)?.Value,
219 | };
220 | result.Items.Add(manifestItem);
221 | }
222 | }
223 | }
224 | return result;
225 | }
226 |
227 | private static Spine ParseSpine(XElement element)
228 | {
229 | Spine result = null;
230 |
231 | if (element != null && element.Name.LocalName == ELEMENT_SPINE)
232 | {
233 | result = new Spine
234 | {
235 | Id = element.Attribute(ATTRIBUTE_ID)?.Value,
236 | Toc = element.Attribute(ATTRIBUTE_TOC)?.Value,
237 | PageProgressionDirection = element.Attribute(ATTRIBUTE_PAGEPROGRESSIONDIRECTION)?.Value,
238 | };
239 | foreach (var childElement in element.Elements())
240 | {
241 | if (childElement.Name.LocalName == ELEMENT_ITEMREF)
242 | {
243 | var spineItemRef = new SpineItemRef
244 | {
245 | IdRef = childElement.Attribute(ATTRIBUTE_IDREF)?.Value,
246 | Linear = childElement.Attribute(ATTRIBUTE_LINEAR)?.Value,
247 | Id = childElement.Attribute(ATTRIBUTE_ID)?.Value,
248 | Properties = childElement.Attribute(ATTRIBUTE_PROPERTIES)?.Value,
249 | };
250 | result.Items.Add(spineItemRef);
251 | }
252 | }
253 | }
254 | return result;
255 | }
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Mobi/MobiReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using Roler.Toolkit.File.Mobi.Compression;
7 | using Roler.Toolkit.File.Mobi.Engine;
8 | using Roler.Toolkit.File.Mobi.Entity;
9 |
10 | namespace Roler.Toolkit.File.Mobi
11 | {
12 | public class MobiReader : IDisposable
13 | {
14 | private bool _disposed;
15 | private readonly Stream _stream;
16 | private readonly IList _palmDBRecordList = new List();
17 |
18 | public MobiReader(Stream stream)
19 | {
20 | this._stream = stream;
21 | }
22 |
23 | #region Methods
24 |
25 | public Mobi ReadWithoutText()
26 | {
27 | if (this._disposed)
28 | {
29 | throw new ObjectDisposedException("stream");
30 | }
31 |
32 | var structure = this.ReadStructure();
33 | var result = new Mobi
34 | {
35 | Structure = structure,
36 | Title = structure.FullName,
37 | };
38 |
39 | if (structure.ExthHeader != null)
40 | {
41 | var exthHeader = structure.ExthHeader;
42 | result.Creator = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Author)?.DataAsString();
43 | result.Publisher = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Publisher)?.DataAsString();
44 | result.Description = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Description)?.DataAsString();
45 | result.Subject = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Subject)?.DataAsString();
46 | result.Date = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.PublishingDate)?.DataAsString();
47 | result.Contributor = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Contributor)?.DataAsString();
48 | result.Rights = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Rights)?.DataAsString();
49 | result.Type = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Type)?.DataAsString();
50 | result.Source = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Source)?.DataAsString();
51 | result.Language = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.Language)?.DataAsString();
52 |
53 | }
54 |
55 | this.RefreshPalmDBRecordList(structure.PalmDB.RecordInfoList);
56 |
57 | result.Cover = this.ReadCover(structure);
58 |
59 | return result;
60 | }
61 |
62 | public bool TryReadWithoutText(out Mobi mobi)
63 | {
64 | bool result;
65 |
66 | try
67 | {
68 | mobi = this.ReadWithoutText();
69 | result = true;
70 | }
71 | catch (Exception)
72 | {
73 | mobi = null;
74 | result = false;
75 | }
76 |
77 | return result;
78 | }
79 |
80 | public Mobi Read(MobiReadingConfiguration configuration = null)
81 | {
82 | var result = this.ReadWithoutText();
83 | result.Text = this.ReadText(result.Structure, configuration);
84 |
85 | return result;
86 | }
87 |
88 | public bool TryRead(out Mobi mobi)
89 | {
90 | bool result;
91 |
92 | try
93 | {
94 | mobi = this.Read();
95 | result = true;
96 | }
97 | catch (Exception)
98 | {
99 | mobi = null;
100 | result = false;
101 | }
102 |
103 | return result;
104 | }
105 |
106 | public string ReadText(Structure structure, MobiReadingConfiguration configuration = null)
107 | {
108 | if (this._disposed)
109 | {
110 | throw new ObjectDisposedException("stream");
111 | }
112 | if (structure is null)
113 | {
114 | throw new ArgumentNullException(nameof(structure));
115 | }
116 | configuration = configuration ?? new MobiReadingConfiguration();
117 |
118 | var decompressedByteList = new List();
119 | ICompression compression = null;
120 | Encoding encoding = Encoding.UTF8;
121 | ushort maxRecordSize = 4096;
122 |
123 | if (structure.PalmDOCHeader != null)
124 | {
125 | if (structure.PalmDOCHeader.RecordSize > 0)
126 | {
127 | maxRecordSize = structure.PalmDOCHeader.RecordSize;
128 | }
129 | switch (structure.PalmDOCHeader.Compression)
130 | {
131 | case CompressionType.PalmDOC:
132 | {
133 | compression = new PalmDocCompression();
134 | }
135 | break;
136 | case CompressionType.HUFF_CDIC:
137 | {
138 | compression = CreateHuffCdicCompression(structure.MobiHeader);
139 | }
140 | break;
141 | default:
142 | {
143 | compression = new NoneCompression();
144 | }
145 | break;
146 | }
147 | }
148 |
149 | if (structure.MobiHeader != null)
150 | {
151 | var firstTextRecordIndex = configuration.FindFirstTextRecordIndex(structure.MobiHeader);
152 | var firstNonTextRecordIndex = configuration.FindFirstNonTextRecordIndex(structure.MobiHeader, this._palmDBRecordList.Count);
153 | for (int i = firstTextRecordIndex; i < firstNonTextRecordIndex; i++)
154 | {
155 | var recordBytes = this.ReadPalmDBRecord(this._palmDBRecordList[i]);
156 | var decompressedBytes = compression.Decompress(recordBytes);
157 | var fixedDecompressedBytes = decompressedBytes.Length > maxRecordSize ?
158 | decompressedBytes.Take(maxRecordSize) :
159 | decompressedBytes;
160 | decompressedByteList.AddRange(fixedDecompressedBytes);
161 | }
162 | }
163 |
164 | return encoding.GetString(decompressedByteList.ToArray());
165 | }
166 |
167 | public Stream ReadContentFile(ContentFile contentFile)
168 | {
169 | if (this._disposed)
170 | {
171 | throw new ObjectDisposedException("stream");
172 | }
173 | if (contentFile is null)
174 | {
175 | throw new ArgumentNullException(nameof(contentFile));
176 | }
177 |
178 | if (int.TryParse(contentFile.Source, out int index) && index < this._palmDBRecordList.Count)
179 | {
180 | byte[] bytes = this.ReadPalmDBRecord(this._palmDBRecordList[index]);
181 | if (bytes != null)
182 | {
183 | return new MemoryStream(bytes);
184 | }
185 | }
186 | return null;
187 | }
188 |
189 | #region Structure
190 |
191 | private Structure ReadStructure()
192 | {
193 | var result = new Structure();
194 | var palmDB = PalmDBEngine.Read(this._stream) ?? throw new InvalidDataException("file can not open.");
195 | result.PalmDB = palmDB;
196 | if (palmDB.RecordInfoList.Any())
197 | {
198 | long firstRecordOffset = palmDB.RecordInfoList.First().Offset;
199 | result.PalmDOCHeader = PalmDOCHeaderEngine.Read(this._stream, firstRecordOffset) ?? throw new InvalidDataException("invalid file! missing part:PalmDOC Header.");
200 |
201 | if (MobiHeaderEngine.TryRead(this._stream, this._stream.Position, out MobiHeader mobiHeader))
202 | {
203 | result.MobiHeader = mobiHeader;
204 | }
205 | else
206 | {
207 | throw new InvalidDataException("invalid file! missing part:MOBI Header.");
208 | }
209 |
210 | if (ExthHeaderEngine.TryRead(this._stream, this._stream.Position, out ExthHeader exthHeader))
211 | {
212 | result.ExthHeader = exthHeader;
213 | }
214 |
215 | if (mobiHeader.FullNameLength > 0)
216 | {
217 | long fullNameOffset = firstRecordOffset + mobiHeader.FullNameOffset;
218 | this._stream.Seek(fullNameOffset, SeekOrigin.Begin);
219 | if (this._stream.TryReadString((int)mobiHeader.FullNameLength, out string fullName))
220 | {
221 | result.FullName = fullName;
222 | }
223 | }
224 |
225 | if (mobiHeader.INDXRecordOffset != MobiHeaderEngine.UnavailableIndex &&
226 | mobiHeader.INDXRecordOffset < palmDB.RecordInfoList.Count &&
227 | IndxHeaderEngine.TryRead(this._stream, palmDB.RecordInfoList[(int)mobiHeader.INDXRecordOffset].Offset, out IndxHeader indxHeader))
228 | {
229 | result.IndxHeader = indxHeader;
230 | }
231 |
232 | if (mobiHeader.FLISRecordOffset != MobiHeaderEngine.UnavailableIndex &&
233 | mobiHeader.FLISRecordOffset < palmDB.RecordInfoList.Count &&
234 | RecordEngine.TryReadFlisRecord(this._stream, palmDB.RecordInfoList[(int)mobiHeader.FLISRecordOffset].Offset, out FlisRecord flisRecord))
235 | {
236 | result.FlisRecord = flisRecord;
237 | }
238 |
239 | if (mobiHeader.FCISRecordOffset != MobiHeaderEngine.UnavailableIndex &&
240 | mobiHeader.FCISRecordOffset < palmDB.RecordInfoList.Count &&
241 | RecordEngine.TryReadFcisRecord(this._stream, palmDB.RecordInfoList[(int)mobiHeader.FCISRecordOffset].Offset, out FcisRecord fcisRecord))
242 | {
243 | result.FcisRecord = fcisRecord;
244 | }
245 |
246 | }
247 |
248 | return result;
249 | }
250 |
251 | #endregion
252 |
253 | #region Text
254 |
255 | private void RefreshPalmDBRecordList(IList palmDBRecordInfoList)
256 | {
257 | this._palmDBRecordList.Clear();
258 | PalmDBRecord lastRecord = null;
259 | foreach (var palmDBRecordInfo in palmDBRecordInfoList)
260 | {
261 | var record = new PalmDBRecord(palmDBRecordInfo);
262 | if (lastRecord != null)
263 | {
264 | lastRecord.Length = (int)(palmDBRecordInfo.Offset - lastRecord.Info.Offset);
265 | }
266 | this._palmDBRecordList.Add(record);
267 | lastRecord = record;
268 | }
269 | }
270 |
271 | private HuffCdicCompression CreateHuffCdicCompression(MobiHeader mobiHeader)
272 | {
273 | HuffCdicCompression result = null;
274 | if (mobiHeader.HuffmanRecordOffset != MobiHeaderEngine.UnavailableIndex &&
275 | mobiHeader.HuffmanRecordOffset < this._palmDBRecordList.Count)
276 | {
277 | var huff = this.ReadPalmDBRecord(this._palmDBRecordList[(int)mobiHeader.HuffmanRecordOffset]);
278 |
279 | var cdicList = new List();
280 | for (int i = 1; i < mobiHeader.HuffmanRecordCount; i++)
281 | {
282 | var recordBytes = this.ReadPalmDBRecord(this._palmDBRecordList[(int)mobiHeader.HuffmanRecordOffset + i]);
283 | cdicList.Add(recordBytes);
284 | }
285 |
286 | result = new HuffCdicCompression(huff, cdicList)
287 | {
288 | ExtraFlags = mobiHeader.ExtraRecordDataFlags
289 | };
290 | }
291 | return result;
292 | }
293 |
294 | private byte[] ReadPalmDBRecord(PalmDBRecord palmDBRecord)
295 | {
296 | byte[] result = new byte[palmDBRecord.Length];
297 | this._stream.Seek(palmDBRecord.Info.Offset, SeekOrigin.Begin);
298 | this._stream.Read(result, 0, result.Length);
299 | return result;
300 | }
301 |
302 | #endregion
303 |
304 | #region Cover
305 |
306 | private ContentFile ReadCover(Structure structure)
307 | {
308 | if (structure is null)
309 | {
310 | throw new ArgumentNullException(nameof(structure));
311 | }
312 |
313 | ContentFile result = null;
314 | if (structure.MobiHeader != null &&
315 | structure.MobiHeader.FirstImageIndex != MobiHeaderEngine.UnavailableIndex &&
316 | structure.MobiHeader.FirstImageIndex < this._palmDBRecordList.Count &&
317 | structure.ExthHeader != null)
318 | {
319 | var exthHeader = structure.ExthHeader;
320 | var coverOffsetBytes = exthHeader.RecordList.FirstOrDefault(p => p.Type == ExthRecordType.CoverOffset)?.Data;
321 | if (coverOffsetBytes != null)
322 | {
323 | var coverImageIndex = structure.MobiHeader.FirstImageIndex + coverOffsetBytes.ToUInt32();
324 | if (coverImageIndex < this._palmDBRecordList.Count)
325 | {
326 | result = new ContentFile(String.Empty, coverImageIndex.ToString());
327 | }
328 | }
329 | }
330 | return result;
331 | }
332 |
333 | #endregion
334 |
335 | #region Disposable
336 |
337 | protected virtual void Dispose(bool disposing)
338 | {
339 | if (_disposed)
340 | {
341 | return;
342 | }
343 |
344 | if (disposing)
345 | {
346 | }
347 |
348 | _disposed = true;
349 | }
350 |
351 | public void Dispose()
352 | {
353 | this.Dispose(true);
354 | GC.SuppressFinalize(this);
355 | }
356 |
357 | #endregion
358 |
359 | #endregion
360 | }
361 | }
362 |
--------------------------------------------------------------------------------
/RolerFileToolkit/Roler.Toolkit.File.Epub/EpubReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Linq;
6 | using Roler.Toolkit.File.Epub.Define;
7 | using Roler.Toolkit.File.Epub.Engine;
8 | using Roler.Toolkit.File.Epub.Entity;
9 | using Roler.Toolkit.File.Epub.Helper;
10 |
11 | namespace Roler.Toolkit.File.Epub
12 | {
13 | public class EpubReader : IDisposable
14 | {
15 | private const string SEPARATOR = ",";
16 | private const string COVER_PROPERTY = "cover-image";
17 | private const string COVER_NAME = "cover";
18 | private const char HREF_SEPARATOR = '#';
19 |
20 | private bool _disposed;
21 | private readonly ZipArchive _archive;
22 |
23 | public EpubReader(Stream stream)
24 | {
25 | this._archive = new ZipArchive(stream);
26 | }
27 |
28 | #region Methods
29 |
30 | public Epub Read()
31 | {
32 | if (this._disposed)
33 | {
34 | throw new ObjectDisposedException("archive");
35 | }
36 |
37 | var structure = this.ReadStructure();
38 | var metadata = structure.Package.Metadata;
39 | var result = new Epub
40 | {
41 | Structure = structure,
42 | Contributor = String.Join(SEPARATOR, metadata.Contributors.Select(p => p.Value)),
43 | Coverage = String.Join(SEPARATOR, metadata.Coverages.Select(p => p.Value)),
44 | Creator = String.Join(SEPARATOR, metadata.Creators.Select(p => p.Value)),
45 | Date = metadata.Date?.Value,
46 | Description = String.Join(SEPARATOR, metadata.Descriptions.Select(p => p.Value)),
47 | Format = String.Join(SEPARATOR, metadata.Formats.Select(p => p.Value)),
48 | Identifier = String.Join(SEPARATOR, metadata.Identifiers.Select(p => p.Value)),
49 | Language = String.Join(SEPARATOR, metadata.Languages.Select(p => p.Value)),
50 | Publisher = String.Join(SEPARATOR, metadata.Publishers.Select(p => p.Value)),
51 | Relation = String.Join(SEPARATOR, metadata.Relations.Select(p => p.Value)),
52 | Rights = String.Join(SEPARATOR, metadata.Rights.Select(p => p.Value)),
53 | Source = String.Join(SEPARATOR, metadata.Sources.Select(p => p.Value)),
54 | Subject = String.Join(SEPARATOR, metadata.Subjects.Select(p => p.Value)),
55 | Title = String.Join(SEPARATOR, metadata.Titles.Select(p => p.Value)),
56 | Type = String.Join(SEPARATOR, metadata.Types.Select(p => p.Value)),
57 | };
58 |
59 | var opfDirectory = Path.GetDirectoryName(structure.Container.FullPath);
60 |
61 | result.Cover = FindCoverFile(structure.Package, opfDirectory);
62 | FillChapters(result.Chapters, structure, opfDirectory);
63 | FillReadingFiles(result.ReadingFiles, structure.Package, opfDirectory);
64 | FillAllFiles(result.AllFiles, structure.Package, opfDirectory);
65 |
66 | return result;
67 | }
68 |
69 | public bool TryRead(out Epub epub)
70 | {
71 | bool result;
72 |
73 | try
74 | {
75 | epub = this.Read();
76 | result = true;
77 | }
78 | catch (Exception)
79 | {
80 | epub = null;
81 | result = false;
82 | }
83 |
84 | return result;
85 | }
86 |
87 | public Stream ReadContentFile(string filePath)
88 | {
89 | if (this._disposed)
90 | {
91 | throw new ObjectDisposedException("archive");
92 | }
93 |
94 | if (!String.IsNullOrWhiteSpace(filePath))
95 | {
96 | var zipEntry = this._archive.GetEntry(filePath);
97 | if (zipEntry != null)
98 | {
99 | return zipEntry.Open();
100 | }
101 | }
102 | return Stream.Null;
103 | }
104 |
105 | public string ReadContentAsText(string filePath)
106 | {
107 | using (var stream = this.ReadContentFile(filePath))
108 | {
109 | using (var streamReader = new StreamReader(stream))
110 | {
111 | return streamReader.ReadToEnd();
112 | }
113 | }
114 | }
115 |
116 | #region Structure
117 |
118 | private Structure ReadStructure()
119 | {
120 | var container = ReadFileFromArchive(this._archive, Const.CONTAINER_PATH, p => ContainerEngine.Read(p));
121 | if (container == null)
122 | {
123 | throw new InvalidDataException("container file not found.");
124 | }
125 |
126 | Structure result = new Structure
127 | {
128 | Container = container,
129 | Package = ReadFileFromArchive(this._archive, container.FullPath, p => OpfEngine.Read(p)) ?? throw new InvalidDataException("opf file not found."),
130 | };
131 |
132 | var opfDirectory = Path.GetDirectoryName(container.FullPath);
133 | if (result.Package.Version >= 3f)
134 | {
135 | string relativePath = FindNavFilePath(result.Package);
136 | var filePath = PathHelper.Combine(opfDirectory, relativePath);
137 | result.Nav = ReadFileFromArchive(this._archive, filePath, p => NavEngine.Read(p)) ?? throw new InvalidDataException("navigation file not found.");
138 | }
139 | else
140 | {
141 | var relativePath = FindNcxFilePath(result.Package);
142 | var filePath = PathHelper.Combine(opfDirectory, relativePath);
143 | result.Ncx = ReadFileFromArchive(this._archive, filePath, p => NcxEngine.Read(p)) ?? throw new InvalidDataException("ncx file not found.");
144 | }
145 |
146 | return result;
147 | }
148 |
149 | private static T ReadFileFromArchive(ZipArchive zipArchive, string filePath, Func func)
150 | {
151 | if (zipArchive != null && !String.IsNullOrWhiteSpace(filePath) && func != null)
152 | {
153 | var zipEntry = zipArchive.GetEntry(filePath);
154 | if (zipEntry != null && zipEntry.Length > 0)
155 | {
156 | using (Stream stream = zipEntry.Open())
157 | {
158 | return func(stream);
159 | }
160 | }
161 | }
162 | return default(T);
163 | }
164 |
165 | private static string FindNavFilePath(Package package)
166 | {
167 | if (package != null && package.Manifest != null && package.Manifest.Items.Any())
168 | {
169 | var mainfestItem = package.Manifest.Items.FirstOrDefault(p => p.IsPropertiesContains("nav"));
170 | return mainfestItem?.Href;
171 | }
172 | return null;
173 | }
174 |
175 | private static string FindNcxFilePath(Package package)
176 | {
177 | if (package != null && package.Spine != null && !String.IsNullOrWhiteSpace(package.Spine.Toc) && package.Manifest != null)
178 | {
179 | var mainfestItem = package.Manifest.Items.FirstOrDefault(p => p.Id == package.Spine.Toc);
180 | return mainfestItem?.Href;
181 | }
182 | return null;
183 | }
184 |
185 | #endregion
186 |
187 | #region Cover
188 |
189 | private static ContentFile FindCoverFile(Package package, string opfDirectory)
190 | {
191 | var coverItem = FindCoverManifestItem(package);
192 | if (coverItem != null)
193 | {
194 | return new ContentFile(coverItem.MediaType, PathHelper.Combine(opfDirectory, coverItem.Href));
195 | }
196 | return null;
197 | }
198 |
199 | private static ManifestItem FindCoverManifestItem(Package package)
200 | {
201 | if (package != null && package.Manifest != null && package.Manifest.Items.Any())
202 | {
203 | if (package.Version >= 3f)
204 | {
205 | return package.Manifest.Items.FirstOrDefault(p => p.IsPropertiesContains(COVER_PROPERTY));
206 | }
207 | else
208 | {
209 | var manifestItemId = package.Metadata?.Metas?.FirstOrDefault(p => p.Name == COVER_NAME)?.Content;
210 | if (!String.IsNullOrWhiteSpace(manifestItemId))
211 | {
212 | return package.Manifest.Items.FirstOrDefault(p => p.Id == manifestItemId);
213 | }
214 | }
215 | }
216 | return null;
217 | }
218 |
219 | #endregion
220 |
221 | #region Chapters
222 |
223 | private static void FillChapters(IList chapters, Structure structure, string opfDirectory)
224 | {
225 | if (chapters != null && structure != null && structure.Package != null)
226 | {
227 | if (structure.Package.Version >= 3f)
228 | {
229 | FillChatpersByNav(chapters, structure.Nav, opfDirectory);
230 | }
231 | else
232 | {
233 | FillChaptersByNcx(chapters, structure.Ncx, opfDirectory);
234 | }
235 | }
236 | }
237 |
238 | private static void FillChatpersByNav(IList chapters, Nav nav, string opfDirectory)
239 | {
240 | if (chapters != null && nav != null)
241 | {
242 | var navLis = nav.NavElements.FirstOrDefault(p => !p.IsHidden)?.Ol?.Items?.Where(p => !p.IsHidden);
243 | if (navLis != null && navLis.Any())
244 | {
245 | foreach (var li in navLis)
246 | {
247 | chapters.Add(GetChapterFromNavLi(li, opfDirectory));
248 | }
249 | }
250 | }
251 | }
252 |
253 | private static void FillChaptersByNcx(IList chapters, Ncx ncx, string opfDirectory)
254 | {
255 | if (chapters != null && ncx != null && ncx.NavMap != null && ncx.NavMap.NavPoints != null)
256 | {
257 | foreach (var navPoint in ncx.NavMap.NavPoints)
258 | {
259 | chapters.Add(GetChapterFromNavPoint(navPoint, opfDirectory));
260 | }
261 | }
262 | }
263 |
264 | private static Chapter GetChapterFromNavLi(NavLi li, string opfDirectory)
265 | {
266 | Chapter result = null;
267 | if (li != null && !li.IsHidden)
268 | {
269 | result = new Chapter();
270 | if (li.A != null)
271 | {
272 | result.Title = String.IsNullOrWhiteSpace(li.A.Value) ? li.A.Title : li.A.Value;
273 | if (!String.IsNullOrWhiteSpace(li.A.Href))
274 | {
275 | var hrefSplit = li.A.Href.Split(new char[] { HREF_SEPARATOR });
276 | result.ContentFilePath = PathHelper.Combine(opfDirectory, hrefSplit[0]);
277 | if (hrefSplit.Length > 1)
278 | {
279 | result.SecondPath = hrefSplit[1];
280 | }
281 | }
282 | }
283 | else if (li.Span != null)
284 | {
285 | result.Title = li.Span.Value;
286 | }
287 |
288 | if (li.Ol != null && !li.Ol.IsHidden && li.Ol.Items.Any())
289 | {
290 | foreach (var liItem in li.Ol.Items)
291 | {
292 | result.Chapters.Add(GetChapterFromNavLi(liItem, opfDirectory));
293 | }
294 | }
295 | }
296 | return result;
297 | }
298 |
299 | private static Chapter GetChapterFromNavPoint(NavPoint navPoint, string opfDirectory)
300 | {
301 | Chapter result = null;
302 | if (navPoint != null)
303 | {
304 | result = new Chapter { Title = navPoint.Label?.Text };
305 | if (!String.IsNullOrWhiteSpace(navPoint.Content?.Source))
306 | {
307 | var hrefSplit = navPoint.Content.Source.Split(new char[] { HREF_SEPARATOR });
308 | result.ContentFilePath = PathHelper.Combine(opfDirectory, hrefSplit[0]);
309 | if (hrefSplit.Length > 1)
310 | {
311 | result.SecondPath = hrefSplit[1];
312 | }
313 | }
314 | if (navPoint.Children != null)
315 | {
316 | foreach (var chid in navPoint.Children)
317 | {
318 | result.Chapters.Add(GetChapterFromNavPoint(chid, opfDirectory));
319 | }
320 | }
321 | }
322 | return result;
323 | }
324 |
325 | #endregion
326 |
327 | #region ReadingFiles
328 |
329 | private static void FillReadingFiles(IList readingFiles, Package package, string opfDirectory)
330 | {
331 | if (readingFiles != null && package != null && package.Spine != null && package.Manifest != null)
332 | {
333 | foreach (var spineItem in package.Spine.Items)
334 | {
335 | var manifestItem = package.Manifest.Items.FirstOrDefault(p => p.Id == spineItem.IdRef);
336 | if (manifestItem != null)
337 | {
338 | readingFiles.Add(new ContentFile(manifestItem.MediaType, PathHelper.Combine(opfDirectory, manifestItem.Href)));
339 | }
340 | }
341 | }
342 | }
343 |
344 | #endregion
345 |
346 | #region AllFiles
347 |
348 | private static void FillAllFiles(IList allFiles, Package package, string opfDirectory)
349 | {
350 | if (allFiles != null && package != null && package.Manifest != null)
351 | {
352 | foreach (var manifestItem in package.Manifest.Items)
353 | {
354 | allFiles.Add(new ContentFile(manifestItem.MediaType, PathHelper.Combine(opfDirectory, manifestItem.Href)));
355 | }
356 | }
357 | }
358 |
359 | #endregion
360 |
361 | #region Disposable
362 |
363 | protected virtual void Dispose(bool disposing)
364 | {
365 | if (_disposed)
366 | {
367 | return;
368 | }
369 |
370 | if (disposing)
371 | {
372 | this._archive.Dispose();
373 | }
374 |
375 | _disposed = true;
376 | }
377 |
378 | public void Dispose()
379 | {
380 | this.Dispose(true);
381 | GC.SuppressFinalize(this);
382 | }
383 |
384 | #endregion
385 |
386 | #endregion
387 | }
388 | }
389 |
--------------------------------------------------------------------------------