Click here to Skip to main content
15,886,199 members
Articles / Desktop Programming / WPF

C.B.R.

Rate me:
Please Sign up or sign in to vote.
4.96/5 (52 votes)
22 Oct 2012GPL329 min read 124.3K   1.8K   132  
Comic and electronic publication reader with library management, extended file conversion, and devices support.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CBR.Core.Models;
using CBR.Core.Helpers;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows;
using System.Windows.Threading;
using System.Threading;
using System.Windows.Media.Imaging;
using SevenZip;
using CBR.Core.Files;
using System.Text.RegularExpressions;

namespace CBR.Core.Services
{
	public class BookService : BookServiceBase
	{
		override internal void LoadCoverThread(object param)
		{
			SevenZipExtractor temp = null;
			Book bk = param as Book;

			try
			{
				temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
				bk.Size = temp.PackedSize / 1024 / 1024;
				bk.PageCount = temp.ArchiveFileNames.Count;

				foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
				{
                    if (!fil.IsDirectory && FileService.Instance.ImageExtension.Contains(Path.GetExtension(fil.FileName).ToUpper()))
					{
                        using (MemoryStream stream = new MemoryStream())
                        {
                            temp.ExtractFile(fil.FileName, stream);

                            using (MemoryStream stream2 = new MemoryStream())
                            {
                                stream.WriteTo(stream2);
                                stream.Flush();
                                stream.Close();
                                stream2.Position = 0;

                                Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate
                                {
                                    BitmapImage myImage = new BitmapImage();
                                    myImage.BeginInit();
                                    myImage.StreamSource = stream2;
                                    myImage.CacheOption = BitmapCacheOption.OnLoad;
                                    myImage.DecodePixelWidth = 70;
                                    myImage.EndInit();

                                    bk.Cover = myImage;
                                });

                                stream2.Flush();
                                stream2.Close();
                            }
                        }
						return;
					}
				}
			}
			catch (Exception err)
			{
                ExceptionHelper.Manage("BookService:LoadCoverThread", err);
			}
			finally
			{
                ZipHelper.Instance.ReleaseExtractor(temp);
			}
		}

        private int CheckFileNames(string fileName)
        {
            Regex MyRegex = new Regex("\\d+",
    RegexOptions.IgnoreCase
    | RegexOptions.Multiline
    | RegexOptions.CultureInvariant
    | RegexOptions.IgnorePatternWhitespace
    | RegexOptions.Compiled
    );

                FileInfo fi = new FileInfo(fileName);
                MatchCollection ms = MyRegex.Matches(fi.Name);
                Match m = ms[ms.Count - 1];
                return Convert.ToInt32(m.Value);
        }

		override public object LoadBook(Book bk)
		{
			SevenZipExtractor temp = null;
			try
			{
                base.LoadBook(bk);

                temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
                bk.Size = temp.PackedSize / 1024 / 1024;
                bk.PageCount = temp.ArchiveFileNames.Count;

				foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
				{
					if (!fil.IsDirectory)
					{
						if (FileService.Instance.ImageExtension.Contains(Path.GetExtension(fil.FileName).ToUpper()))
						{
							Page item = new Page(bk, fil.FileName, bk.Pages.Count);
                            item.Index = CheckFileNames(fil.FileName);
							bk.Pages.Add(item);
						}
					}
				}

                bk.Pages = bk.Pages.OrderBy(p => p.Index).ToList<Page>();

                foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
				{
					if (!fil.IsDirectory && fil.FileName.Contains(".dynamics.xml"))
					{
                        Page pg = bk.Pages.Find(p => p.FileName == fil.FileName.Replace(".dynamics.xml", ""));
                        if (pg != null)
                        {
                            MemoryStream stream = new MemoryStream();
                            temp.ExtractFile(fil.FileName, stream);

                            MemoryStream stream2 = new MemoryStream();
                            stream.WriteTo(stream2);
                            stream.Flush();
                            stream.Close();
                            stream2.Position = 0;

                            pg.Frames = (List<Zone>)XmlHelper.Deserialize(stream2, typeof(List<Zone>));

                            stream2.Close();

                            if (pg.Frames == null) pg.Frames = new List<Zone>();
                        }
					}
				}
			}
			catch (Exception err)
			{
                ExceptionHelper.Manage("BookService:LoadBook", err);
			}
			finally
			{
                ZipHelper.Instance.ReleaseExtractor(temp);
			}
			return bk.Pages[0];
		}

        #region -----------------BOOK MANAGEMENT-----------------

        override public void EditBook(Book bk)
		{
			SevenZipExtractor temp = null;
			try
			{
                temp = ZipHelper.Instance.GetExtractor(bk.FilePath);

				foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
				{
					if (!fil.IsDirectory)
					{
						if (fil.FileName.Contains(".dynamics.xml"))
						{
							Page item = new Page(bk, fil.FileName, bk.Pages.Count);
							bk.Pages.Add(item);
						}
					}
				}
			}
			catch (Exception err)
			{
                ExceptionHelper.Manage("BookService:EditBook", err);
			}
			finally
			{
                ZipHelper.Instance.ReleaseExtractor(temp);
			}
		}

        override public Book SaveBook(Book bk)
        {
            try
            {
                //first time we save as dynamic book
                if (IsDynamic(bk) && Path.GetExtension(bk.FilePath) != ".dcb")
                {
                    string newFile = FirstSaveDynamicBook(bk);
                    UnloadBook(bk);
                    return CreateBook(newFile);
                }
                else //only update the frame files
                {
                    UpdateDynamicBook(bk);
                }
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:SaveBook", err);
            }
            return null;
        }

        internal string FirstSaveDynamicBook(Book bk)
        {
            try
            {
                // create a temp folder
                string tempFolder = Path.Combine(DirectoryHelper.ApplicationPath, Path.GetFileNameWithoutExtension(bk.FilePath));
                DirectoryHelper.Check(tempFolder);

                //extract the book content
                ExtractBook(bk, tempFolder);

                //serialize all frames even empty to create the files in the zip
                foreach (Page pg in bk.Pages)
                {
                    XmlHelper.Serialize(Path.Combine(tempFolder, pg.FileName + ".dynamics.xml"), pg.Frames);
                }

                // create a new file by compressing all temp folder content
                string newComic = bk.FilePath.Replace(Path.GetExtension(bk.FilePath), ".dcb");

                SevenZip.SevenZipCompressor cp = new SevenZip.SevenZipCompressor();
                cp.ArchiveFormat = SevenZip.OutArchiveFormat.Zip;

                string[] outputFiles = new DirectoryInfo(tempFolder).GetFiles("*.*").Select(p => p.FullName).ToArray();

                using (FileStream fs = new FileStream(newComic, FileMode.Create))
                {
                    cp.CompressFiles(fs, outputFiles);
                }

                //delete the temp folder
                Directory.Delete(tempFolder, true);

                return newComic;
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:FirstSaveDynamicBook", err);
            }
            return null;
        }

        internal void UpdateDynamicBook(Book bk)
        {
            try
            {
                
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:UpdateDynamicBook", err);
            }
        }

        internal Dictionary<int, string> GetBookContentList(Book bk)
        {
            SevenZipExtractor temp = null;
            Dictionary<int, string> content = new Dictionary<int, string>();
            try
            {
                temp = ZipHelper.Instance.GetExtractor(bk.FilePath);

                foreach (ArchiveFileInfo fil in temp.ArchiveFileData)
                {
                    if (!fil.IsDirectory)
                    {
                        content.Add(content.Count, fil.FileName);
                    }
                }
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:GetBookContentList", err);
            }
            finally
            {
                ZipHelper.Instance.ReleaseExtractor(temp);
            }

            return content;
        }

        internal void ExtractBook(Book bk, string outputFolder)
        {
            SevenZipExtractor temp = null;
            try
            {
                temp = ZipHelper.Instance.GetExtractor(bk.FilePath);
                temp.PreserveDirectoryStructure = false;
                temp.ExtractArchive(outputFolder);
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:ExtractBook", err);
            }
            finally
            {
                ZipHelper.Instance.ReleaseExtractor(temp);
            }
        }

       
		#endregion

		public BitmapImage GetImageFromStream(string zipFilePath, string fileName)
		{
            SevenZipExtractor temp = null;
            BitmapImage result = null;
			try
			{
                temp = ZipHelper.Instance.GetExtractor(zipFilePath);

			    MemoryStream stream = new MemoryStream();
                temp.ExtractFile(fileName, stream);

			    result = StreamToImage.GetImageFromStreamBug(stream, 0);
            }
            catch (Exception err)
            {
                ExceptionHelper.Manage("BookService:GetImageFromStream", err);
            }
            finally
            {
                ZipHelper.Instance.ReleaseExtractor(temp);
            }
            return result;
        }

		#region -----------------BOOKMARK-----------------

		override public void SetMark(Book bk, Page pg)
		{
            if (bk != null && pg != null)
		    	bk.Bookmark = pg.FilePath;
		}

        override public Page GotoMark(Book bk)
		{
            if (bk != null)
            {
                if (!string.IsNullOrEmpty(bk.Bookmark))
                {
                    foreach (Page pg in bk.Pages)
                    {
                        if (pg.FilePath == bk.Bookmark)
                        {
                            return pg;
                        }
                    }
                }
            }
            
            return null;
		}

		#endregion

		#region -----------------page navigation management-----------------

		override public Page GotoPage(Book bk, Page currentPage, int step)
		{
            Page result = base.GotoPage( bk, currentPage, step );

			if (step == 1)
			{
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate
                {
                    PreparePageCache(result);
                });
            }

            return result;
		}

		public bool CheckPageRange(Book bk, Page currentPage, int step)
		{
			int pos = 0;

			if (currentPage != null)
				pos = bk.Pages.IndexOf(currentPage);

			if (step == -1)
			{
				return (pos == 0) ? false : true;
			}
			else
			{
				return (pos >= bk.Pages.Count - 1) ? false : true;
			}
		}

        public void GotoFrame(Book bk, ref Page currentPage, ref Zone currentFrame, int step)
        {
            currentFrame = GotoFrame(currentPage, currentFrame, step);

            //we reach the min or max, change the page
            if (currentFrame == null)
                currentPage = GotoPage(bk, currentPage, step);

            GotoFrame(currentPage, currentFrame, step);
        }

        internal Zone GotoFrame(Page currentPage, Zone currentFrame, int step)
        {
            //if no frames, return a page frame
            if (currentPage.Frames.Count <= 0)
            {
                if (currentFrame == null)
                    return new Zone() { Duration = 15, OrderNum = 0, X = 0, Y = 0, Type = FrameType.Page };
                else
                    return null;
            }

            //search current frame position
            int posFrame = 0;
            if (currentFrame != null)
                posFrame = currentPage.Frames.IndexOf(currentFrame);

            //we go back, manage the min
            if (step == -1)
            {
                return (posFrame == 0) ? null : currentPage.Frames[posFrame - 1];
            }
            else //we go forward, manage the max
            {
                return (posFrame >= currentPage.Frames.Count - 1) ? null : currentPage.Frames[posFrame + 1];
            }
        }

		#endregion

        override public long ManageCache(Book bk)
		{
            int counter = bk.Pages.Where(p => p.ImageExist == true).Count();

            //select and delete if more than cache count
            IEnumerable<Page> filter = bk.Pages
                        .Where(p => p.ImageExist == true)
                        .OrderBy(p => p.Index).Take(counter - WorkspaceService.Instance.Settings.ImageCacheCount);

            foreach (Page item in filter)
                item.Image = null;

            //select and delete if over cache duration
            filter = bk.Pages
                        .Where(p => p.ImageExist == true &&
                                p.ImageLastAcces < DateTime.Now.AddMinutes(-WorkspaceService.Instance.Settings.ImageCacheDuration))
                        .OrderBy(p => p.Index);

            foreach (Page item in filter)
                item.Image = null;

            //return total size in cache
			return bk.Pages.Where(p => p.ImageExist == true).Sum(f => f.Image.StreamSource.Length) / 1024 / 1024;
		}

        internal void PreparePageCache(object threadParam)
        {
            Page currentPage = threadParam as Page;

            if (currentPage != null)
            {
                //load the 3 next pages after current page
                BitmapImage tmpImage = null;

                Page tmpPage = GetNextPage(currentPage);
                if (tmpPage != null) tmpImage = tmpPage.Image;

                tmpPage = GetNextPage(tmpPage);
                if (tmpPage != null) tmpImage = tmpPage.Image;

                tmpPage = GetNextPage(tmpPage);
                if (tmpPage != null) tmpImage = tmpPage.Image;
            }
        }

        internal Page GetNextPage(Page currentPage)
        {
            if (currentPage != null)
            {
                Book parent = currentPage.Parent;

                if (parent != null)
                {
                    int next = parent.Pages.IndexOf(currentPage);
                    if (next >= parent.Pages.Count - 1)
                        return null;
                    else
                    {
                        next = next + 1;
                        return parent.Pages[next];
                    }
                }
                else return null;
            }
            else return null;
        }
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Architect
France France
WPF and MVVM fan, I practice C # in all its forms from the beginning of the NET Framework without mentioning C ++ / MFC and other software packages such as databases, ASP, WCF, Web & Windows services, Application, and now Core and UWP.
In my wasted hours, I am guilty of having fathered C.B.R. and its cousins C.B.R. for WinRT and UWP on the Windows store.
But apart from that, I am a great handyman ... the house, a rocket stove to heat the jacuzzi and the last one: a wood oven for pizza, bread, and everything that goes inside

https://guillaumewaser.wordpress.com/
https://fouretcompagnie.wordpress.com/

Comments and Discussions