From e16c621b9a49cc386b3b65e7fde1f25eb780e85b Mon Sep 17 00:00:00 2001 From: Michael Vrhel Date: Mon, 10 Feb 2014 09:51:04 -0800 Subject: Significant amount of content needed for gsview This includes the interface to ghostscript for creating pdf files from ps for mupdf to then render in the viewer as well as the capability to print the output of xpswrite through the windows print queue. Added thumb viewing and navigation as well as navigation through the table of contents. To do. Add in password handling, Text Search, Hyperlinks, GS conversion to other file formats, MuPDF save as other file formats, Page extractions, proper page selection for printing, Zooming (rescale), Fix the page ranges that are displayed at full resolution during navigation. --- platform/winrt/gsview/ContentEntry.cs | 30 + platform/winrt/gsview/ContentItem.cs | 42 ++ platform/winrt/gsview/Convert.xaml | 113 ++++ platform/winrt/gsview/Convert.xaml.cs | 170 +++++ platform/winrt/gsview/DocPage.cs | 132 ++++ platform/winrt/gsview/Links.cs | 33 + platform/winrt/gsview/MainWindow.xaml | 371 ++++++++++- platform/winrt/gsview/MainWindow.xaml.cs | 790 +++++++++++++++++++++-- platform/winrt/gsview/Properties/AssemblyInfo.cs | 2 +- platform/winrt/gsview/RectList.cs | 65 ++ platform/winrt/gsview/Resources/Left.ico | Bin 0 -> 29926 bytes platform/winrt/gsview/Resources/Right.ico | Bin 0 -> 29926 bytes platform/winrt/gsview/Resources/contents.ico | Bin 0 -> 4286 bytes platform/winrt/gsview/Resources/gsview.ico | Bin 0 -> 2486 bytes platform/winrt/gsview/Resources/gsview.png | Bin 0 -> 2654 bytes platform/winrt/gsview/Resources/hyperlink.png | Bin 0 -> 3531 bytes platform/winrt/gsview/Resources/printer.ico | Bin 23558 -> 55326 bytes platform/winrt/gsview/Resources/search.ico | Bin 0 -> 67646 bytes platform/winrt/gsview/Resources/search.png | Bin 0 -> 3491 bytes platform/winrt/gsview/Resources/thumbnail.ico | Bin 0 -> 1150 bytes platform/winrt/gsview/Resources/zoom_in.ico | Bin 0 -> 67646 bytes platform/winrt/gsview/Resources/zoom_out.ico | Bin 0 -> 67646 bytes platform/winrt/gsview/ghostsharp.cs | 453 +++++++++++++ platform/winrt/gsview/gsIO.cs | 33 + platform/winrt/gsview/gsOutput.xaml | 35 + platform/winrt/gsview/gsOutput.xaml.cs | 54 ++ platform/winrt/gsview/gsprint.cs | 144 +++++ platform/winrt/gsview/gsview.csproj | 67 +- platform/winrt/gsview/gsview.ico | Bin 0 -> 2486 bytes platform/winrt/gsview/mudocument.cs | 235 +++++++ platform/winrt/munet/munet.cpp | 165 +++++ platform/winrt/munet/munet.h | 41 ++ platform/winrt/munet/munet.vcxproj | 208 ++++++ platform/winrt/munet/munet.vcxproj.filters | 42 ++ platform/winrt/mupdf.sln | 65 +- platform/winrt/mupdfnet/mupdfnet.vcxproj | 207 ------ platform/winrt/mupdfnet/mupdfnet.vcxproj.filters | 21 - platform/winrt/mupdfnet/pch.cpp | 6 - platform/winrt/mupdfnet/pch.h | 15 - platform/winrt/mupdfnet/targetver.h | 8 - platform/winrt/mupdfwinrt/muctx.cpp | 15 + platform/winrt/mupdfwinrt/muctx.h | 14 +- 42 files changed, 3220 insertions(+), 356 deletions(-) create mode 100644 platform/winrt/gsview/ContentEntry.cs create mode 100644 platform/winrt/gsview/ContentItem.cs create mode 100644 platform/winrt/gsview/Convert.xaml create mode 100644 platform/winrt/gsview/Convert.xaml.cs create mode 100644 platform/winrt/gsview/DocPage.cs create mode 100644 platform/winrt/gsview/Links.cs create mode 100644 platform/winrt/gsview/RectList.cs create mode 100644 platform/winrt/gsview/Resources/Left.ico create mode 100644 platform/winrt/gsview/Resources/Right.ico create mode 100644 platform/winrt/gsview/Resources/contents.ico create mode 100644 platform/winrt/gsview/Resources/gsview.ico create mode 100644 platform/winrt/gsview/Resources/gsview.png create mode 100644 platform/winrt/gsview/Resources/hyperlink.png create mode 100644 platform/winrt/gsview/Resources/search.ico create mode 100644 platform/winrt/gsview/Resources/search.png create mode 100644 platform/winrt/gsview/Resources/thumbnail.ico create mode 100644 platform/winrt/gsview/Resources/zoom_in.ico create mode 100644 platform/winrt/gsview/Resources/zoom_out.ico create mode 100644 platform/winrt/gsview/ghostsharp.cs create mode 100644 platform/winrt/gsview/gsIO.cs create mode 100644 platform/winrt/gsview/gsOutput.xaml create mode 100644 platform/winrt/gsview/gsOutput.xaml.cs create mode 100644 platform/winrt/gsview/gsprint.cs create mode 100644 platform/winrt/gsview/gsview.ico create mode 100644 platform/winrt/gsview/mudocument.cs create mode 100644 platform/winrt/munet/munet.cpp create mode 100644 platform/winrt/munet/munet.h create mode 100644 platform/winrt/munet/munet.vcxproj create mode 100644 platform/winrt/munet/munet.vcxproj.filters delete mode 100644 platform/winrt/mupdfnet/mupdfnet.vcxproj delete mode 100644 platform/winrt/mupdfnet/mupdfnet.vcxproj.filters delete mode 100644 platform/winrt/mupdfnet/pch.cpp delete mode 100644 platform/winrt/mupdfnet/pch.h delete mode 100644 platform/winrt/mupdfnet/targetver.h (limited to 'platform') diff --git a/platform/winrt/gsview/ContentEntry.cs b/platform/winrt/gsview/ContentEntry.cs new file mode 100644 index 00000000..68600170 --- /dev/null +++ b/platform/winrt/gsview/ContentEntry.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace gsview +{ + public class ContentEntry + { + public String Name + { + get; + set; + } + + public int PageNum + { + get; + set; + } + + public ContentEntry(String Name, int PageNum) + { + this.Name = Name; + this.PageNum = PageNum; + } + }; +} + diff --git a/platform/winrt/gsview/ContentItem.cs b/platform/winrt/gsview/ContentItem.cs new file mode 100644 index 00000000..a9e079c8 --- /dev/null +++ b/platform/winrt/gsview/ContentItem.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.ComponentModel; + +namespace gsview +{ + class ContentItem : INotifyPropertyChanged + { + + public int Page + { + get; + internal set; + } + + public String StringMargin + { + get; + internal set; + } + + public ContentItem() + { + StringMargin = ""; + Page = 0; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void ContentRefresh() + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("StringMargin")); + PropertyChanged(this, new PropertyChangedEventArgs("Page")); + } + } + } +} diff --git a/platform/winrt/gsview/Convert.xaml b/platform/winrt/gsview/Convert.xaml new file mode 100644 index 00000000..68a007d2 --- /dev/null +++ b/platform/winrt/gsview/Convert.xaml @@ -0,0 +1,113 @@ + + + + + + + + + + + + Devices: + + + + + + + + + + + + + + + + + + + + Pages: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Options: + + + + + + + diff --git a/platform/winrt/gsview/Convert.xaml.cs b/platform/winrt/gsview/Convert.xaml.cs new file mode 100644 index 00000000..30c1b465 --- /dev/null +++ b/platform/winrt/gsview/Convert.xaml.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.ComponentModel; +using System.Collections.ObjectModel; + +namespace gsview +{ + public class Device : INotifyPropertyChanged + { + public String DeviceName + { + get; + internal set; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void PageRefresh() + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("DeviceName")); + } + } + + public Device() + { + this.DeviceName = ""; + } + }; + + public class SelectPage : INotifyPropertyChanged + { + public int Page + { + get; + internal set; + } + + public int PageString + { + get; + internal set; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void PageRefresh() + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("Page")); + } + } + }; + + + /// + /// Interaction logic for Convert.xaml + /// + public partial class Convert : Window + { + List GSDevices; + List ConvertPages; + public Convert(int num_pages) + { + InitializeComponent(); + GSDevices = new List(); + ConvertPages = new List(); + SetDeviceList(); + SetPageList(num_pages); + xaml_DeviceList.ItemsSource = GSDevices; + xaml_PageList.ItemsSource = ConvertPages; + } + + public void SetDeviceList() + { + foreach (gsDevice_t device in Enum.GetValues(typeof(gsDevice_t))) + { + Device device_t = new Device(); + device_t.DeviceName = Enum.GetName(typeof(gsDevice_t), device); + GSDevices.Add(device_t); + } + } + + public void SetPageList(int num_pages) + { + for (int k = 1; k < num_pages + 1; k++ ) + { + SelectPage Spage = new SelectPage(); + Spage.Page = k; + Spage.PageString = k; + ConvertPages.Add(Spage); + } + } + + private void ConvertClick(object sender, RoutedEventArgs e) + { + + } + + private void ConvertCancel(object sender, RoutedEventArgs e) + { + + } + + private void HelpConvert(object sender, RoutedEventArgs e) + { + + } + + private void AllPages(object sender, RoutedEventArgs e) + { + xaml_PageList.SelectAll(); + } + + private void EvenPages(object sender, RoutedEventArgs e) + { + /* First check if any are selected */ + var item = xaml_PageList.SelectedItem; + + /* If none are selected then get all the evens. otherwise just get + * all the evens of the pages that have been selected */ + if (item == null) + { + /* Turn on the evens */ + for (int kk = 1; kk < ConvertPages.Count; kk = kk + 2) + (xaml_PageList.ItemContainerGenerator.ContainerFromIndex(kk) as ListViewItem).IsSelected = true; + } + else + { + /* Turn off any odds */ + for (int kk = 0; kk < ConvertPages.Count; kk = kk + 2) + (xaml_PageList.ItemContainerGenerator.ContainerFromIndex(kk) as ListViewItem).IsSelected = false; + } + } + + private void OddPages(object sender, RoutedEventArgs e) + { + /* First check if any are selected */ + var item = xaml_PageList.SelectedItem; + + /* If none are selected then get all the odds. otherwise just get + * all the odds of the pages that have been selected */ + if (item == null) + { + /* Turn on the odds */ + for (int kk = 0; kk < ConvertPages.Count; kk = kk + 2) + (xaml_PageList.ItemContainerGenerator.ContainerFromIndex(kk) as ListViewItem).IsSelected = true; + } + else + { + /* Turn off any evens */ + for (int kk = 1; kk < ConvertPages.Count; kk = kk + 2) + (xaml_PageList.ItemContainerGenerator.ContainerFromIndex(kk) as ListViewItem).IsSelected = false; + } + } + } +} diff --git a/platform/winrt/gsview/DocPage.cs b/platform/winrt/gsview/DocPage.cs new file mode 100644 index 00000000..1cf55112 --- /dev/null +++ b/platform/winrt/gsview/DocPage.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.ComponentModel; +using System.Windows.Media.Imaging; +using System.Collections.ObjectModel; +using System.Windows.Media; + +namespace gsview +{ + public class DocPage : INotifyPropertyChanged + { + public int Height + { + get; + internal set; + } + + public int Width + { + get; + internal set; + } + + + public int NativeHeight + { + get; + set; + } + + public int NativeWidth + { + get; + set; + } + + public double Zoom + { + get; + set; + } + + public BitmapSource BitMap + { + get; + set; + } + + public List TextBox + { + get; + set; + } + + public List LinkBox + { + get; + set; + } + + public Page_Content_t Content + { + get; + set; + } + + public String PageName + { + get; + set; + } + + public int PageNum + { + get; + set; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void PageRefresh() + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("BitMap")); + PropertyChanged(this, new PropertyChangedEventArgs("Height")); + PropertyChanged(this, new PropertyChangedEventArgs("Width")); + PropertyChanged(this, new PropertyChangedEventArgs("TextBox")); + } + } + + public DocPage() + { + this.Height = 0; + this.Width = 0; + this.NativeHeight = 0; + this.NativeWidth = 0; + this.Zoom = 0; + this.BitMap = null; + this.TextBox = null; + this.LinkBox = null; + this.Content = Page_Content_t.NOTSET; + this.PageNum = -1; + this.PageName = ""; + } + + public DocPage(int Height, int Width, double Zoom, BitmapSource BitMap, + List TextBox, List LinkBox, + Page_Content_t Content, int PageNum) + { + this.Height = Height; + this.Width = Width; + this.Zoom = Zoom; + this.BitMap = BitMap; + this.TextBox = TextBox; + this.LinkBox = LinkBox; + this.Content = Content; + this.PageNum = PageNum; + this.PageName = ("Page " + (PageNum + 1)); + } + }; + public class Pages : ObservableCollection + { + public Pages() + : base() + { + } + } +} diff --git a/platform/winrt/gsview/Links.cs b/platform/winrt/gsview/Links.cs new file mode 100644 index 00000000..822176d8 --- /dev/null +++ b/platform/winrt/gsview/Links.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Drawing; + +namespace gsview +{ + public enum link_t + { + LINK_GOTO = 0, + LINK_URI, + TEXTBOX, /* Do double duty with this class */ + NOT_SET, + }; + + class Links + { + link_t type; + //PointF upper_left; + //PointF lower_right; + Uri uri; + int page_num; + + public Links() + { + uri = new Uri(""); + page_num = -1; + type = link_t.NOT_SET; + } + } +} diff --git a/platform/winrt/gsview/MainWindow.xaml b/platform/winrt/gsview/MainWindow.xaml index 5706c547..47a1ef25 100644 --- a/platform/winrt/gsview/MainWindow.xaml +++ b/platform/winrt/gsview/MainWindow.xaml @@ -1,17 +1,74 @@  - - - + Title="gsview" Height="800" Width="650"> + + + + + + + + + + + + + + + + + + + + - + @@ -20,14 +77,14 @@ - + - + @@ -40,7 +97,7 @@ - + @@ -70,18 +127,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Creating Thumbs + + + + + + + + + + + Searching + + + + + + + + + + + + Printing + + - - - - + + + + + + + + + Distilling + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/platform/winrt/gsview/MainWindow.xaml.cs b/platform/winrt/gsview/MainWindow.xaml.cs index 1e23d84e..71726468 100644 --- a/platform/winrt/gsview/MainWindow.xaml.cs +++ b/platform/winrt/gsview/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -13,9 +14,11 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Forms; -using mupdfwinrt; -using System.Threading.Tasks; -using System.Runtime.InteropServices.WindowsRuntime; +using System.ComponentModel; +using System.IO; +using System.Windows.Xps.Packaging; +using System.Printing; +using System.Windows.Markup; enum AppBar_t { @@ -37,7 +40,7 @@ enum RenderingStatus_t REN_PAGE /* Used to ignore value when source based setting */ }; -enum status_t +public enum status_t { S_ISOK, E_FAILURE, @@ -67,7 +70,7 @@ public struct spatial_info_t { public Point size; public double scale_factor; -} ; +}; /* C# has no defines.... */ static class Constants @@ -85,7 +88,7 @@ static class Constants public const int KEY_MINUS = 0xbd; public const int ZOOM_IN = 0; public const int ZOOM_OUT = 1; - public const double screenScale = 1; + public const double SCREEN_SCALE = 1; public const int HEADER_SIZE = 54; public const int SEARCH_FORWARD = 1; public const int SEARCH_BACKWARD = -1; @@ -97,8 +100,18 @@ namespace gsview /// /// Interaction logic for MainWindow.xaml /// + /// + + public struct thumb_t + { + public int page_num; + public Byte[] bitmap; + public Point size; + } + public partial class MainWindow : Window { + mudocument mu_doc; public Pages m_docPages; List m_thumbnails; List> m_page_link_list; @@ -108,37 +121,125 @@ namespace gsview List m_text_list; private int m_rectlist_page; private List m_content_list; - mudocument mu_doc; private bool m_file_open; private int m_currpage; private int m_searchpage; private int m_num_pages; - private int m_slider_min; - private int m_slider_max; private bool m_init_done; - private bool m_flip_from_searchlink; private bool m_links_on; private int m_search_rect_count; private bool m_page_update; - WriteableBitmap m_BlankBmp; String m_textcolor; String m_linkcolor; RenderingStatus_t m_ren_status; - private bool m_insearch; /* Used for UI display */ - private bool m_search_active; /* Used to avoid multiple UI clicks */ - private bool m_sliderchange; - private double m_Progress; - int m_width; - int m_height; + private bool m_insearch; + private bool m_search_active; private bool m_handlingzoom; - private double m_panX; - private double m_panY; private bool m_have_thumbs; + private bool m_have_contents; + double m_doczoom; + ghostsharp m_ghostscript; + String m_currfile; + private gsprint m_ghostprint = null; + bool m_isXPS; + gsOutput m_gsoutput; + Convert m_convertwin; public MainWindow() { InitializeComponent(); + this.Closing += new System.ComponentModel.CancelEventHandler(Window_Closing); + m_file_open = false; + status_t result = CleanUp(); + + /* Allocations */ + try + { + m_docPages = new Pages(); + m_thumbnails = new List(); + m_page_link_list = new List>(); + m_text_list = new List(); + m_linkset = new List(); + m_ghostscript = new ghostsharp(); + m_ghostscript.gsUpdateMain += new ghostsharp.gsCallBackMain(gsProgress); + m_gsoutput = new gsOutput(); + m_gsoutput.Activate(); + m_ghostscript.gsIOUpdateMain += new ghostsharp.gsIOCallBackMain(gsIO); + m_convertwin = null; + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed at initialization\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Out of memory: " + e.Message); + } + } + + void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + m_gsoutput.RealWindowClosing(); + } + + private status_t CleanUp() + { + m_init_done = false; + + /* Clear out everything */ + if (m_docPages != null && m_docPages.Count > 0) + m_docPages.Clear(); + if (m_thumbnails != null && m_thumbnails.Count > 0) + m_thumbnails.Clear(); + if (m_page_link_list != null && m_page_link_list.Count > 0) + m_page_link_list.Clear(); + if (m_text_list != null && m_text_list.Count > 0) + m_text_list.Clear(); + if (m_linkset != null && m_linkset.Count > 0) + m_linkset.Clear(); + + if (mu_doc != null) + mu_doc.CleanUp(); + try + { + mu_doc = new mudocument(); + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed during clean up\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Out of memory: " + e.Message); + } + status_t result = mu_doc.Initialize(); + + if (result != status_t.S_ISOK) + { + Console.WriteLine("Library allocation failed during clean up\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Library allocation failed!"); + return result; + } + + m_have_thumbs = false; m_file_open = false; + m_insearch = false; + m_search_active = false; + m_num_pages = -1; + m_search_rect_count = 0; + m_links_on = false; + m_rectlist_page = -1; + m_doczoom = 1.0; + m_isXPS = false; + return result; + } + + private void ShowMessage(NotifyType_t type, String Message) + { + if (type == NotifyType_t.MESS_ERROR) + { + // System.Windows.Forms.MessageBox.Show(Message, "Error", + // MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } + else + { + // System.Windows.Forms.MessageBox.Show(Message, "Notice", + // MessageBoxButtons.OK); + } } private void CloseDoc() @@ -146,12 +247,157 @@ namespace gsview + } + + /* Set the page with the new raster information */ + private void UpdatePage(int page_num, Byte[] bitmap, Point ras_size, + Page_Content_t content, double zoom_in) + { + DocPage doc_page = this.m_docPages[page_num]; + + doc_page.Width = (int)ras_size.X; + doc_page.Height = (int)ras_size.Y; + + doc_page.Content = content; + doc_page.Zoom = zoom_in; + + int stride = doc_page.Width * 4; + doc_page.BitMap = BitmapSource.Create(doc_page.Width, doc_page.Height, 72, 72, PixelFormats.Pbgra32, BitmapPalettes.Halftone256, bitmap, stride); + doc_page.PageNum = page_num; + + if (content == Page_Content_t.THUMBNAIL) + { + doc_page.Width = (int)(ras_size.X / Constants.SCALE_THUMB); + doc_page.Height = (int)(ras_size.Y / Constants.SCALE_THUMB); + } + } + + void SetThumbInit(int page_num, Byte[] bitmap, Point ras_size, double zoom_in) + { + /* Two jobs. Store the thumb and possibly update the full page */ + DocPage doc_page = m_thumbnails[page_num]; + + doc_page.Width = (int)ras_size.X; + doc_page.Height = (int)ras_size.Y; + doc_page.Content = Page_Content_t.THUMBNAIL; + doc_page.Zoom = zoom_in; + int stride = doc_page.Width * 4; + doc_page.BitMap = BitmapSource.Create(doc_page.Width, doc_page.Height, 72, 72, PixelFormats.Pbgra32, BitmapPalettes.Halftone256, bitmap, stride); + doc_page.PageNum = page_num; + + /* And the main page */ + var doc = m_docPages[page_num]; + if (doc.Content == Page_Content_t.THUMBNAIL || doc.Content == Page_Content_t.FULL_RESOLUTION) + return; + else + { + doc_page = this.m_docPages[page_num]; + doc_page.Content = Page_Content_t.THUMBNAIL; + doc_page.Zoom = zoom_in; + + doc_page.BitMap = m_thumbnails[page_num].BitMap; + doc_page.Width = (int)(ras_size.X / Constants.SCALE_THUMB); + doc_page.Height = (int)(ras_size.Y / Constants.SCALE_THUMB); + doc_page.PageNum = page_num; + } + } + + private void ThumbsWork(object sender, DoWorkEventArgs e) + { + Point ras_size; + status_t code; + double scale_factor; + spatial_info_t spatial_info = InitSpatial(1); + BackgroundWorker worker = sender as BackgroundWorker; + + spatial_info.scale_factor = Constants.SCALE_THUMB; + Byte[] bitmap; + + for (int k = 0; k < m_num_pages; k++) + { + if (ComputePageSize(spatial_info, k, out ras_size, out scale_factor) == status_t.S_ISOK) + { + try + { + bitmap = new byte[(int)ras_size.X * (int)ras_size.Y * 4]; + /* Synchronous call on our background thread */ + code = (status_t) mu_doc.RenderPage(k, bitmap, (int)ras_size.X, (int)ras_size.Y, scale_factor, false, false); + } + catch (OutOfMemoryException em) + { + Console.WriteLine("Memory allocation failed thumb page " + k + em.Message + "\n"); + break; + } + /* Use thumb if we rendered ok */ + if (code == status_t.S_ISOK) + { + double percent = 100 * (double)(k + 1) / (double)m_num_pages; + thumb_t curr_thumb = new thumb_t(); + curr_thumb.page_num = k; + curr_thumb.bitmap = bitmap; + curr_thumb.size = ras_size; + worker.ReportProgress((int)percent, curr_thumb); + } + } + } + } + + private void ThumbsCompleted(object sender, RunWorkerCompletedEventArgs e) + { + xaml_ProgressGrid.Visibility = System.Windows.Visibility.Collapsed; + xaml_ThumbProgress.Value = 0; + xaml_ThumbList.ItemsSource = m_thumbnails; + m_have_thumbs = true; + } + + private void ThumbsProgressChanged(object sender, ProgressChangedEventArgs e) + { + thumb_t thumb = (thumb_t)(e.UserState); + + xaml_ThumbProgress.Value = e.ProgressPercentage; + SetThumbInit(thumb.page_num, thumb.bitmap, thumb.size, 1.0); + m_docPages[thumb.page_num].PageRefresh(); + m_thumbnails[thumb.page_num].PageRefresh(); + } + + private void RenderThumbs() + { + /* Create background task for rendering the thumbnails. Allow + this to be cancelled if we open a new doc while we are in loop + rendering. Put the UI updates in the progress changed which will + run on the main thread */ + try + { + BackgroundWorker worker = new BackgroundWorker(); + worker.WorkerReportsProgress = true; + worker.WorkerSupportsCancellation = true; + worker.DoWork += new DoWorkEventHandler(ThumbsWork); + worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ThumbsCompleted); + worker.ProgressChanged += new ProgressChangedEventHandler(ThumbsProgressChanged); + xaml_ProgressGrid.Visibility = System.Windows.Visibility.Visible; + worker.RunWorkerAsync(); + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed during thumb rendering\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Out of memory: " + e.Message); + } + } + + private spatial_info_t InitSpatial(double scale) + { + spatial_info_t value = new spatial_info_t(); + + value.size.Y = this.ActualHeight; + value.size.X = this.ActualWidth; + value.scale_factor = scale; + return value; } private void OpenFile(object sender, RoutedEventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); - dlg.Filter = "Xps Documents (*.xps)|*.xps"; + dlg.Filter = "Document Files(*.ps;*.eps;*.pdf;*.xps;*.cbz)|*.ps;*.eps;*.pdf;*.xps;*.cbz|All files (*.*)|*.*"; dlg.FilterIndex = 1; if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) { @@ -159,38 +405,502 @@ namespace gsview { CloseDoc(); } - try + /* If we have a ps or eps file then launch the distiller first + * and then we will get a temp pdf file which will be opened by + * mupdf */ + string extension = System.IO.Path.GetExtension(dlg.FileName); + if (extension.ToUpper() == ".PS" || extension.ToUpper() == ".EPS") + { + xaml_DistillProgress.Value = 0; + if (m_ghostscript.DistillPS(dlg.FileName) == gsStatus.GS_BUSY) + { + ShowMessage(NotifyType_t.MESS_STATUS, "GS currently busy"); + return; + } + xaml_DistillGrid.Visibility = System.Windows.Visibility.Visible; + return; + } + + /* Set if this is already xps for printing */ + if (extension.ToUpper() == ".XPS") + m_isXPS = true; + + StartViewer(dlg.FileName); + + } + } + + private void StartViewer(String File) + { + m_currfile = File; + status_t code = mu_doc.OpenFile(m_currfile); + if (code == status_t.S_ISOK) + { + InitialRender(); + RenderThumbs(); + m_file_open = true; + } + else + { + m_currfile = null; + ShowMessage(NotifyType_t.MESS_ERROR, "Failed to open file!"); + } + } + + private status_t ComputePageSize(spatial_info_t spatial_info, int page_num, + out Point render_size, out double scale_factor) + { + Point screenSize = new Point(); + Point renpageSize = new Point(); + + status_t code = (status_t)mu_doc.GetPageSize(page_num, out render_size); + if (code != status_t.S_ISOK) + { + scale_factor = 1.0; + return code; + } + + screenSize = spatial_info.size; + screenSize.Y *= Constants.SCREEN_SCALE; + screenSize.X *= Constants.SCREEN_SCALE; + + double hscale = screenSize.X / render_size.X; + double vscale = screenSize.Y / render_size.Y; + double scale = Math.Min(hscale, vscale); + renpageSize.X = (render_size.X * scale * spatial_info.scale_factor); + renpageSize.Y = (render_size.Y * scale * spatial_info.scale_factor); + + scale_factor = (scale * spatial_info.scale_factor); + render_size = renpageSize; + + return status_t.S_ISOK; + } + + private DocPage InitDocPage() + { + DocPage doc_page = new DocPage(); + + doc_page.BitMap = null; + doc_page.Height = Constants.BLANK_HEIGHT; + doc_page.Width = Constants.BLANK_WIDTH; + doc_page.NativeHeight = Constants.BLANK_HEIGHT; + doc_page.NativeWidth = Constants.BLANK_WIDTH; + doc_page.Content = Page_Content_t.DUMMY; + doc_page.TextBox = null; + doc_page.LinkBox = null; + return doc_page; + } + + async private void InitialRender() + { + m_num_pages = mu_doc.GetPageCount(); + m_currpage = 0; + + for (int k = 0; k < m_num_pages; k++) + { + m_docPages.Add(InitDocPage()); + m_thumbnails.Add(InitDocPage()); + /* Create empty lists for our links and specify that they have + not been computed for these pages */ + List temp_link = new List(); + m_page_link_list.Add(temp_link); + m_linkset.Add(false); + } + + /* Do the first few full res pages */ + spatial_info_t spatial_info = InitSpatial(1); + for (int k = 0; k < Constants.LOOK_AHEAD + 2; k++) + { + if (m_num_pages > k) { - OpenDocument(dlg.FileName); + Point ras_size; + double scale_factor; + + if (ComputePageSize(spatial_info, k, out ras_size, out scale_factor) == status_t.S_ISOK) + { + try + { + Byte[] bitmap = new byte[(int)ras_size.X * (int)ras_size.Y * 4]; + + Task ren_task = + new Task(() => mu_doc.RenderPage(k, bitmap, (int)ras_size.X, (int)ras_size.Y, scale_factor, false, true)); + ren_task.Start(); + await ren_task.ContinueWith((antecedent) => + { + status_t code = (status_t)ren_task.Result; + if (code == status_t.S_ISOK) + UpdatePage(k, bitmap, ras_size, Page_Content_t.FULL_RESOLUTION, 1.0); + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed page " + k + "\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Out of memory: " + e.Message); + } + } } - catch (UnauthorizedAccessException) + } + m_init_done = true; + xaml_PageList.ItemsSource = m_docPages; + } + + private void OnBackPageClick(object sender, RoutedEventArgs e) + { + if (m_currpage == 0) return; + + m_currpage = m_currpage - 1; + xaml_PageList.ScrollIntoView(m_docPages[m_currpage]); + } + + private void OnForwardPageClick(object sender, RoutedEventArgs e) + { + if (m_currpage == m_num_pages - 1) return; + + m_currpage = m_currpage + 1; + xaml_PageList.ScrollIntoView(m_docPages[m_currpage]); + } + + private void CancelLoadClick(object sender, RoutedEventArgs e) + { + /* Cancel during thumbnail loading. Deactivate the button + * and cancel the thumbnail rendering */ + } + + private void ToggleThumbs(object sender, RoutedEventArgs e) + { + if (m_have_thumbs) + { + if (xaml_ThumbGrid.Visibility == System.Windows.Visibility.Collapsed) { - System.Windows.MessageBox.Show( - String.Format("Unable to access {0}", dlg.FileName)); + xaml_ThumbGrid.Visibility = System.Windows.Visibility.Visible; + } + else + { + xaml_ThumbGrid.Visibility = System.Windows.Visibility.Collapsed; + } + } + } + + private void ToggleContents(object sender, RoutedEventArgs e) + { + if (xaml_ContentGrid.Visibility == System.Windows.Visibility.Visible) + { + xaml_ContentGrid.Visibility = System.Windows.Visibility.Collapsed; + return; + } + + if (m_num_pages < 0) + return; + + if (xaml_ContentList.Items.IsEmpty) + { + int size_content = mu_doc.ComputeContents(); + if (size_content == 0) + return; + xaml_ContentList.ItemsSource = mu_doc.contents; + } + xaml_ContentGrid.Visibility = System.Windows.Visibility.Visible; + } + + private void ThumbSelected(object sender, MouseButtonEventArgs e) + { + var item = ((FrameworkElement)e.OriginalSource).DataContext as DocPage; + if (item != null) + { + xaml_PageList.ScrollIntoView(m_docPages[item.PageNum]); + } + } + + private void ContentSelected(object sender, MouseButtonEventArgs e) + { + var item = ((FrameworkElement)e.OriginalSource).DataContext as ContentItem; + if (item != null && item.Page < m_num_pages) + { + xaml_PageList.ScrollIntoView(m_docPages[item.Page]); + } + } + + private void ListViewScrollChanged(object sender, ScrollChangedEventArgs e) + { + var lv = (System.Windows.Controls.ListView) sender; + foreach (var lvi in lv.Items) + { + var container = lv.ItemContainerGenerator.ContainerFromItem(lvi) as ListBoxItem; + if (container != null && Visible(container, lv)) + { + var found = container.Content; + if (found != null) + { + var Item = (DocPage)found; + RenderRange(Item.PageNum); + } return; } } } - private async void OpenDocument(String filename) + /* Render +/- the look ahead from where we are if blank page is present */ + async private void RenderRange(int curr_page) + { + spatial_info_t spatial_info = InitSpatial(m_doczoom); + int range = Constants.LOOK_AHEAD; + + range = 0; // debug + for (int k = curr_page - range; k <= curr_page + range; k++) + { + if (k >= 0 && k < m_num_pages) + { + /* Check if page is already rendered */ + var doc = m_docPages[k]; + if (doc.Content != Page_Content_t.FULL_RESOLUTION || + doc.Zoom != m_doczoom) + { + Point ras_size; + double scale_factor; + + if (ComputePageSize(spatial_info, k, out ras_size, out scale_factor) == status_t.S_ISOK) + { + try + { + Byte[] bitmap = new byte[(int)ras_size.X * (int)ras_size.Y * 4]; + + Task ren_task = + new Task(() => mu_doc.RenderPage(k, bitmap, (int)ras_size.X, (int)ras_size.Y, scale_factor, false, true)); + ren_task.Start(); + await ren_task.ContinueWith((antecedent) => + { + status_t code = (status_t)ren_task.Result; + if (code == status_t.S_ISOK) + { + UpdatePage(k, bitmap, ras_size, Page_Content_t.FULL_RESOLUTION, 1.0); + m_docPages[k].PageRefresh(); + } + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed page " + k + "\n"); + ShowMessage(NotifyType_t.MESS_ERROR, "Out of memory: " + e.Message); + } + } + } + } + } + } + + private bool Visible(FrameworkElement elem, FrameworkElement cont) + { + if (!elem.IsVisible) + return false; + Rect rect = new Rect(0.0, 0.0, cont.ActualWidth, cont.ActualHeight); + Rect bounds = elem.TransformToAncestor(cont).TransformBounds(new Rect(0.0, 0.0, elem.ActualWidth, elem.ActualHeight)); + Rect bounds2 = new Rect(new Point(bounds.TopLeft.X, bounds.TopLeft.Y), new Point(bounds.BottomRight.X, bounds.BottomRight.Y - 5)); + return rect.Contains(bounds2.TopLeft) || rect.Contains(bounds2.BottomRight); + } + + private void ReleasePages(int old_page, int new_page) + { + if (old_page == new_page) return; + /* To keep from having memory issue reset the page back to + the thumb if we are done rendering the thumbnails */ + for (int k = old_page - Constants.LOOK_AHEAD; k <= old_page + Constants.LOOK_AHEAD; k++) + { + if (k < new_page - Constants.LOOK_AHEAD || k > new_page + Constants.LOOK_AHEAD) + { + if (k >= 0 && k < m_num_pages) + { + SetThumb(k); + } + } + } + } + + /* Return this page from a full res image to the thumb image or only set + to thumb if it has not already been set */ + private void SetThumb(int page_num) + { + /* See what is there now */ + var doc = m_docPages[page_num]; + if (doc.Content == Page_Content_t.THUMBNAIL && doc.Zoom == m_doczoom) return; + + if (m_thumbnails.Count > page_num) + { + m_page_update = true; + var thumb_page = m_thumbnails[page_num]; + thumb_page.Height = (int)(thumb_page.NativeHeight * m_doczoom); + thumb_page.Width = (int)(thumb_page.NativeWidth * m_doczoom); + thumb_page.Zoom = 1.0; + m_docPages[page_num] = thumb_page; + m_page_update = false; + } + } + + private void LinksToggle(object sender, RoutedEventArgs e) { - string target = "."; - char[] anyOf = target.ToCharArray(); - var index = filename.LastIndexOfAny(anyOf); - string extension = filename.Substring(index + 1); + } - int result = mu_doc.OpenFileName(filename, extension); + private void Search(object sender, RoutedEventArgs e) + { - //int result = await mu_doc.OpenFileAsync(filename, extension); - /* Check if we need password */ - //if (mu_doc.RequiresPassword()) - //{ - //SetView(view_t.VIEW_PASSWORD); - // return; - //} - //else - // InitialRender(); + } + + private void ZoomOut(object sender, RoutedEventArgs e) + { + + } + + private void ZoomIn(object sender, RoutedEventArgs e) + { + + } + + private void CancelSearchClick(object sender, RoutedEventArgs e) + { + + } + + private void gsIO(object gsObject, String mess, int len) + { + m_gsoutput.Update(mess, len); + } + + private void gsProgress(object gsObject, gsEventArgs asyncInformation) + { + if (asyncInformation.Completed) + { + xaml_DistillProgress.Value = 100; + xaml_DistillProgress.Visibility = System.Windows.Visibility.Collapsed; + if (asyncInformation.Params.result == GS_Result_t.gsFAILED) + { + switch (asyncInformation.Params.task) + { + case GS_Task_t.CREATE_XPS: + ShowMessage(NotifyType_t.MESS_STATUS, "Ghostscript failed to create XPS"); + break; + + case GS_Task_t.PS_DISTILL: + ShowMessage(NotifyType_t.MESS_STATUS, "Ghostscript failed to distill PS"); + break; + + case GS_Task_t.SAVE_RESULT: + + break; + } + return; + } + GSResult(asyncInformation.Params); + } + else + { + xaml_PrintProgress.Value = asyncInformation.Progress; + } + } + + /* GS Result*/ + public void GSResult(gsParams_t result) + { + switch (result.task) + { + case GS_Task_t.CREATE_XPS: + PrintXPS(result.outputfile); + break; + + case GS_Task_t.PS_DISTILL: + StartViewer(result.outputfile); + break; + + case GS_Task_t.SAVE_RESULT: + + break; + } + } + + + /* Printing is achieved using xpswrite device in ghostscript and + * pushing that file through the XPS print queue */ + private void Print(object sender, RoutedEventArgs e) + { + if (!m_file_open) + return; + + /* If file is already xps then gs need not do this */ + if (!m_isXPS) + { + xaml_DistillProgress.Value = 0; + if (m_ghostscript.CreateXPS(m_currfile) == gsStatus.GS_BUSY) + { + ShowMessage(NotifyType_t.MESS_STATUS, "GS currently busy"); + return; + } + else + { + xaml_DistillGrid.Visibility = System.Windows.Visibility.Visible; + } + + } + PrintXPS(m_currfile); + } + + private void PrintXPS(String file) + { + gsprint ghostprint = new gsprint(); + System.Windows.Controls.PrintDialog pDialog = ghostprint.GetPrintDialog(); + + if (pDialog == null) + return; + + XpsDocument xpsDocument = new XpsDocument(file, FileAccess.Read); + FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence(); + + PrintQueue printQueue = pDialog.PrintQueue; + + m_ghostprint = ghostprint; + xaml_PrintGrid.Visibility = System.Windows.Visibility.Visible; + m_ghostprint.PrintUpdate += new gsprint.AsyncPrintCallBack(PrintProgress); + + xaml_PrintProgress.Value = 0; + + ghostprint.Print(printQueue, fixedDocSeq); + } + + private void PrintProgress(object printHelper, gsPrintEventArgs asyncInformation) + { + if (asyncInformation.Completed) + { + xaml_PrintProgress.Value = 100; + xaml_PrintGrid.Visibility = System.Windows.Visibility.Collapsed; + } + else + { + xaml_PrintProgress.Value = 100 * (double) asyncInformation.Page / (double) m_num_pages; + } + } + + private void CancelDistillClick(object sender, RoutedEventArgs e) + { + + } + + private void CancelPrintClick(object sender, RoutedEventArgs e) + { + m_ghostprint.CancelAsync(); + } + + private void ShowGSMessage(object sender, RoutedEventArgs e) + { + m_gsoutput.Show(); + } + + private void ConvertClick(object sender, RoutedEventArgs e) + { + if (m_convertwin == null) + { + m_convertwin = new Convert(m_num_pages); + m_convertwin.Activate(); + m_convertwin.Show(); + } } } } + diff --git a/platform/winrt/gsview/Properties/AssemblyInfo.cs b/platform/winrt/gsview/Properties/AssemblyInfo.cs index 4cb76a71..77e3ac52 100644 --- a/platform/winrt/gsview/Properties/AssemblyInfo.cs +++ b/platform/winrt/gsview/Properties/AssemblyInfo.cs @@ -12,7 +12,7 @@ using System.Windows; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("gsview")] -[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyCopyright("Copyright © 2014")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/platform/winrt/gsview/RectList.cs b/platform/winrt/gsview/RectList.cs new file mode 100644 index 00000000..3c761876 --- /dev/null +++ b/platform/winrt/gsview/RectList.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace gsview +{ + public class RectList + { + public String Index + { + get; + set; + } + + public String Color + { + get; + set; + } + + public int Height + { + get; + set; + } + + public int Width + { + get; + set; + } + + public int X + { + get; + set; + } + + public int Y + { + get; + set; + } + + public int Type + { + get; + set; + } + + public int PageNum + { + get; + set; + } + + public Uri Urilink + { + get; + set; + } + } +} diff --git a/platform/winrt/gsview/Resources/Left.ico b/platform/winrt/gsview/Resources/Left.ico new file mode 100644 index 00000000..b2507e31 Binary files /dev/null and b/platform/winrt/gsview/Resources/Left.ico differ diff --git a/platform/winrt/gsview/Resources/Right.ico b/platform/winrt/gsview/Resources/Right.ico new file mode 100644 index 00000000..c2f9238f Binary files /dev/null and b/platform/winrt/gsview/Resources/Right.ico differ diff --git a/platform/winrt/gsview/Resources/contents.ico b/platform/winrt/gsview/Resources/contents.ico new file mode 100644 index 00000000..97a12dd8 Binary files /dev/null and b/platform/winrt/gsview/Resources/contents.ico differ diff --git a/platform/winrt/gsview/Resources/gsview.ico b/platform/winrt/gsview/Resources/gsview.ico new file mode 100644 index 00000000..65b8b6b9 Binary files /dev/null and b/platform/winrt/gsview/Resources/gsview.ico differ diff --git a/platform/winrt/gsview/Resources/gsview.png b/platform/winrt/gsview/Resources/gsview.png new file mode 100644 index 00000000..bfcfb8db Binary files /dev/null and b/platform/winrt/gsview/Resources/gsview.png differ diff --git a/platform/winrt/gsview/Resources/hyperlink.png b/platform/winrt/gsview/Resources/hyperlink.png new file mode 100644 index 00000000..6ea0d798 Binary files /dev/null and b/platform/winrt/gsview/Resources/hyperlink.png differ diff --git a/platform/winrt/gsview/Resources/printer.ico b/platform/winrt/gsview/Resources/printer.ico index 34a15747..c03d9853 100644 Binary files a/platform/winrt/gsview/Resources/printer.ico and b/platform/winrt/gsview/Resources/printer.ico differ diff --git a/platform/winrt/gsview/Resources/search.ico b/platform/winrt/gsview/Resources/search.ico new file mode 100644 index 00000000..2841a637 Binary files /dev/null and b/platform/winrt/gsview/Resources/search.ico differ diff --git a/platform/winrt/gsview/Resources/search.png b/platform/winrt/gsview/Resources/search.png new file mode 100644 index 00000000..9bbb2255 Binary files /dev/null and b/platform/winrt/gsview/Resources/search.png differ diff --git a/platform/winrt/gsview/Resources/thumbnail.ico b/platform/winrt/gsview/Resources/thumbnail.ico new file mode 100644 index 00000000..71d38e97 Binary files /dev/null and b/platform/winrt/gsview/Resources/thumbnail.ico differ diff --git a/platform/winrt/gsview/Resources/zoom_in.ico b/platform/winrt/gsview/Resources/zoom_in.ico new file mode 100644 index 00000000..b0ca2ead Binary files /dev/null and b/platform/winrt/gsview/Resources/zoom_in.ico differ diff --git a/platform/winrt/gsview/Resources/zoom_out.ico b/platform/winrt/gsview/Resources/zoom_out.ico new file mode 100644 index 00000000..a31d6c8b Binary files /dev/null and b/platform/winrt/gsview/Resources/zoom_out.ico differ diff --git a/platform/winrt/gsview/ghostsharp.cs b/platform/winrt/gsview/ghostsharp.cs new file mode 100644 index 00000000..50c08b57 --- /dev/null +++ b/platform/winrt/gsview/ghostsharp.cs @@ -0,0 +1,453 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks;using System.Runtime.InteropServices; +using System.IO; +using System.Security; +using System.ComponentModel; + +namespace gsview +{ + public enum gsDevice_t + { + bmp16, + bmp16m, + bmp256, + bmp32b, + bmpgray, + bmpmono, + epswrite, + jpeg, + jpegcmyk, + jpeggray, + pamcmyk32, + pamcmyk4, + pbm, + pgm, + png16, + png16m, + png256, + pngalpha, + pnggray, + pngmono, + pdfwrite, + ps2write, + psdcmyk, + psdrgb, + pxlcolor, + pxlmono, + tiff12nc, + tiff24nc, + tiff32nc, + tiff64nc, + tiffcrle, + tiffg3, + tiffg32d, + tiffg4, + tiffgray, + tifflzw, + tiffpack, + tiffsep, + txtwrite, + xpswrite + }; + + public enum GS_Task_t + { + PS_DISTILL, + CREATE_XPS, + SAVE_RESULT + } + + public enum GS_Result_t + { + gsOK, + gsFAILED + } + + /* Parameters */ + public struct gsParams_t + { + public int resolution; + public gsDevice_t device; + public String outputfile; + public String inputfile; + public GS_Task_t task; + public GS_Result_t result; + }; + + public class gsEventArgs : EventArgs + { + private bool m_completed; + private int m_progress; + private gsParams_t m_param; + + public bool Completed + { + get { return m_completed; } + } + + public gsParams_t Params + { + get { return m_param; } + } + + public int Progress + { + get { return m_progress; } + } + + public gsEventArgs(bool completed, int progress, gsParams_t param) + { + m_completed = completed; + m_progress = progress; + m_param = param; + } + } + + /* from gs */ + struct gsapi_revision_t + { + IntPtr product; + IntPtr copyright; + long revision; + long revisiondate; + } + + public enum gsEncoding { + GS_ARG_ENCODING_LOCAL = 0, + GS_ARG_ENCODING_UTF8 = 1, + GS_ARG_ENCODING_UTF16LE = 2 + }; + + public enum gsStatus + { + GS_READY, + GS_BUSY, + GS_ERROR + }; + + [SuppressUnmanagedCodeSecurity] + class ghostsharp + { + /* Callback proto for stdio */ + public delegate int gsStdIOHandler(IntPtr caller_handle, IntPtr buffer, + int len); + + /* Ghostscript API */ + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_revision(IntPtr stuct, int size); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_new_instance(out IntPtr pinstance, + IntPtr caller_handle); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern void gsapi_delete_instance(IntPtr instance); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_init_with_args(IntPtr instance, int argc, + IntPtr argv); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_exit(IntPtr instance); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_set_arg_encoding(IntPtr instance, + int encoding); + + [DllImport("gsdll64.dll", CharSet = CharSet.Ansi, + CallingConvention = CallingConvention.StdCall)] + public static extern int gsapi_set_stdio(IntPtr instance, + gsStdIOHandler stdin, gsStdIOHandler stdout, gsStdIOHandler stderr); + + private int StdInCallback(IntPtr handle, IntPtr pointer, int count) + { + String output = Marshal.PtrToStringAnsi(pointer); + return count; + } + + private int StdOutCallback(IntPtr handle, IntPtr pointer, int count) + { + String output = Marshal.PtrToStringAnsi(pointer); + gsIOUpdateMain(this, output, count); + return count; + } + + private int StdErrCallback(IntPtr handle, IntPtr pointer, int count) + { + String output = Marshal.PtrToStringAnsi(pointer); + gsIOUpdateMain(this, output, count); + return count; + } + + IntPtr gsInstance; + BackgroundWorker m_worker; + /* Callbacks to Main */ + internal delegate void gsIOCallBackMain(object gsObject, String mess, int len); + internal event gsIOCallBackMain gsIOUpdateMain; + internal delegate void gsCallBackMain(object gsObject, gsEventArgs info); + internal event gsCallBackMain gsUpdateMain; + + public ghostsharp() + { + m_worker = null; + gsInstance = IntPtr.Zero; + } + + /* A standard command line approach to using gs API */ + private void gsWork1(object sender, DoWorkEventArgs e) + { + gsParams_t Params = (gsParams_t) e.Argument; + String out_file = Params.outputfile; + String in_file = Params.inputfile; + int num_params = 9; + var argParam = new GCHandle[num_params]; + var argPtrs = new IntPtr[num_params]; + String[] strParams = new String[num_params]; + List CharacterArray = new List(num_params); + int e_Quit = -101; + GCHandle argPtrsStable; + + /* New instance */ + int code = gsapi_new_instance(out gsInstance, IntPtr.Zero); + if (code < 0) + { + Params.result = GS_Result_t.gsFAILED; + e.Result = Params; + return; + } + + var RaiseStdInCallback = new gsStdIOHandler(StdInCallback); + var RaiseStdOutCallback = new gsStdIOHandler(StdOutCallback); + var RaiseStdErrCallback = new gsStdIOHandler(StdErrCallback); + + var stdInPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdInCallback); + var stdOutPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdOutCallback); + var stdErrPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdErrCallback); + + // Setup stdio callback functions + //code = gsapi_set_stdio(gsInstance, stdInPtr, stdOutPtr, stdErrPtr); + code = gsapi_set_stdio(gsInstance, RaiseStdInCallback, RaiseStdOutCallback, RaiseStdErrCallback); + + code = gsapi_set_arg_encoding(gsInstance, (int) gsEncoding.GS_ARG_ENCODING_UTF8); + if (code == 0) + { + strParams[0] = "gs"; /* This does not matter */ + strParams[1] = "-dNOPAUSE"; + strParams[2] = "-dBATCH"; + strParams[3] = "-dSAFER"; + strParams[4] = "-sDEVICE=" + Enum.GetName(typeof(gsDevice_t), Params.device); + strParams[5] = "-r" + Params.resolution; + /* Create temp file if file not specified */ + if (out_file == null) + { + out_file = Path.GetTempFileName(); + Params.outputfile = out_file; + } + strParams[6] = "-o" + out_file; + strParams[7] = "-f"; + strParams[8] = in_file; + + /* Now convert our Strings to char* and get pinned handles to these. + * This keeps the c# GC from moving stuff around on us */ + for (int k = 0; k < num_params; k++) + { + CharacterArray.Add(System.Text.Encoding.UTF8.GetBytes(strParams[k].ToCharArray())); + argParam[k] = GCHandle.Alloc(CharacterArray[k], GCHandleType.Pinned); + argPtrs[k] = argParam[k].AddrOfPinnedObject(); + } + /* Also stick the array of pointers into memory that will not be GCd */ + argPtrsStable = GCHandle.Alloc(argPtrs, GCHandleType.Pinned); + + code = gsapi_init_with_args(gsInstance, num_params, argPtrsStable.AddrOfPinnedObject()); + + /* All the pinned items need to be freed so the GC can do its job */ + for (int k = 0; k < num_params; k++) + { + argParam[k].Free(); + } + argPtrsStable.Free(); + } + + int code1 = gsapi_exit(gsInstance); + if ((code == 0) || (code == e_Quit)) + code = code1; + + RaiseStdInCallback = null; + RaiseStdOutCallback = null; + RaiseStdErrCallback = null; + + gsapi_delete_instance(gsInstance); + if ((code == 0) || (code == e_Quit)) + { + Params.result = GS_Result_t.gsOK; + e.Result = Params; + return; + } + + Params.result = GS_Result_t.gsFAILED; + e.Result = Params; + return; + } + + /* Feeding gs piecemeal so that we can have some progress callback */ + private void gsWork2(object sender, DoWorkEventArgs e) + { + gsParams_t Params = (gsParams_t)e.Argument; + String out_file = Params.outputfile; + String in_file = Params.inputfile; + int num_params = 10; + var argParam = new GCHandle[num_params]; + var argPtrs = new IntPtr[num_params]; + String[] strParams = new String[num_params]; + List CharacterArray = new List(num_params); + int e_Quit = -101; + GCHandle argPtrsStable; + + /* New instance */ + int code = gsapi_new_instance(out gsInstance, IntPtr.Zero); + if (code < 0) + { + Params.result = GS_Result_t.gsFAILED; + e.Result = Params; + return; + } + + code = gsapi_set_arg_encoding(gsInstance, (int)gsEncoding.GS_ARG_ENCODING_UTF8); + if (code == 0) + { + strParams[0] = "gs"; /* This does not matter */ + strParams[1] = "-dNOPAUSE"; + strParams[2] = "-dBATCH"; + strParams[3] = "-dSAFER"; + strParams[4] = "-sDEVICE=" + Enum.GetName(typeof(gsDevice_t), Params.device); + strParams[5] = "-r" + Params.resolution; + strParams[6] = "-sstdout=%sstderr"; /* Need to get setup to capture stdout and stderr */ + /* Create temp file if file not specified */ + if (out_file == null) + { + out_file = Path.GetTempFileName(); + Params.outputfile = out_file; + } + strParams[7] = "-sOutputFile=" + out_file; + strParams[8] = "-f"; + strParams[9] = in_file; + + /* Now convert our Strings to char* and get pinned handles to these. + * This keeps the c# GC from moving stuff around on us */ + for (int k = 0; k < num_params; k++) + { + CharacterArray.Add(System.Text.Encoding.UTF8.GetBytes(strParams[k].ToCharArray())); + argParam[k] = GCHandle.Alloc(CharacterArray[k], GCHandleType.Pinned); + argPtrs[k] = argParam[k].AddrOfPinnedObject(); + } + /* Also stick the array of pointers into memory that will not be GCd */ + argPtrsStable = GCHandle.Alloc(argPtrs, GCHandleType.Pinned); + + code = gsapi_init_with_args(gsInstance, num_params, argPtrsStable.AddrOfPinnedObject()); + + /* All the pinned items need to be freed so the GC can do its job */ + for (int k = 0; k < num_params; k++) + { + argParam[k].Free(); + } + argPtrsStable.Free(); + } + + int code1 = gsapi_exit(gsInstance); + if ((code == 0) || (code == e_Quit)) + code = code1; + + gsapi_delete_instance(gsInstance); + if ((code == 0) || (code == e_Quit)) + { + Params.result = GS_Result_t.gsOK; + e.Result = Params; + return; + } + + Params.result = GS_Result_t.gsFAILED; + e.Result = Params; + return; + } + + private void gsCompleted(object sender, RunWorkerCompletedEventArgs e) + { + /* Get the result and do the callback */ + gsParams_t Value = (gsParams_t)e.Result; + gsEventArgs info = new gsEventArgs(true, 100, Value); + gsUpdateMain(this, info); + } + + private void gsProgressChanged(object sender, ProgressChangedEventArgs e) + { + /* Get the result and do the callback */ + gsParams_t Value = new gsParams_t(); + gsEventArgs info = new gsEventArgs(false, e.ProgressPercentage, Value); + gsUpdateMain(this, info); + } + + public gsStatus DistillPS(String FileName) + { + gsParams_t Params = new gsParams_t(); ; + + Params.device = gsDevice_t.pdfwrite; + Params.outputfile = null; + Params.resolution = 300; + Params.inputfile = FileName; + Params.outputfile = null; + return RunGhostscript(Params); + } + + public gsStatus CreateXPS(String FileName) + { + gsParams_t Params = new gsParams_t(); ; + + Params.device = gsDevice_t.xpswrite; + Params.outputfile = null; + Params.resolution = 300; + Params.inputfile = FileName; + Params.outputfile = null; + return RunGhostscript(Params); + } + + private gsStatus RunGhostscript(gsParams_t Params) + { + /* Create background task for rendering the thumbnails. Allow + this to be cancelled if we open a new doc while we are in loop + rendering. Put the UI updates in the progress changed which will + run on the main thread */ + try + { + if (m_worker != null) + { + m_worker.CancelAsync(); + return gsStatus.GS_BUSY; + } + m_worker = new BackgroundWorker(); + m_worker.WorkerReportsProgress = true; + m_worker.WorkerSupportsCancellation = true; + m_worker.DoWork += new DoWorkEventHandler(gsWork1); + m_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(gsCompleted); + m_worker.ProgressChanged += new ProgressChangedEventHandler(gsProgressChanged); + m_worker.RunWorkerAsync(Params); + return gsStatus.GS_READY; + } + catch (OutOfMemoryException e) + { + Console.WriteLine("Memory allocation failed during gs rendering\n"); + return gsStatus.GS_ERROR; + } + } + + } +} diff --git a/platform/winrt/gsview/gsIO.cs b/platform/winrt/gsview/gsIO.cs new file mode 100644 index 00000000..831f34f7 --- /dev/null +++ b/platform/winrt/gsview/gsIO.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.ComponentModel; + +namespace gsview +{ + class gsIO : INotifyPropertyChanged + { + public String gsIOString + { + get; + set; + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void PageRefresh() + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("gsIOString")); + } + } + + public gsIO() + { + this.gsIOString = ""; + } + } +} diff --git a/platform/winrt/gsview/gsOutput.xaml b/platform/winrt/gsview/gsOutput.xaml new file mode 100644 index 00000000..0be459a4 --- /dev/null +++ b/platform/winrt/gsview/gsOutput.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/platform/winrt/gsview/gsOutput.xaml.cs b/platform/winrt/gsview/gsOutput.xaml.cs new file mode 100644 index 00000000..90bb2d55 --- /dev/null +++ b/platform/winrt/gsview/gsOutput.xaml.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace gsview +{ + /// + /// Interaction logic for gsOutput.xaml + /// + public partial class gsOutput : Window + { + gsIO m_gsIO; + public gsOutput() + { + InitializeComponent(); + this.Closing += new System.ComponentModel.CancelEventHandler(FakeWindowClosing); + m_gsIO = new gsIO(); + xaml_gsText.DataContext = m_gsIO; + } + + void FakeWindowClosing(object sender, System.ComponentModel.CancelEventArgs e) + { + e.Cancel = true; + this.Hide(); + } + + private void HideWindow(object sender, RoutedEventArgs e) + { + this.Hide(); + } + + public void RealWindowClosing() + { + this.Closing -= new System.ComponentModel.CancelEventHandler(FakeWindowClosing); + this.Close(); + } + + public void Update(String newstring, int len) + { + m_gsIO.gsIOString += newstring.Substring(0, len); + m_gsIO.PageRefresh(); + } + } +} diff --git a/platform/winrt/gsview/gsprint.cs b/platform/winrt/gsview/gsprint.cs new file mode 100644 index 00000000..8fb7ad81 --- /dev/null +++ b/platform/winrt/gsview/gsprint.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Packaging; +using System.Printing; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Documents.Serialization; +using System.Windows.Media; +using System.Windows.Xps; +using System.Windows.Xps.Packaging; +using System.Windows.Xps.Serialization; + +namespace gsview +{ + /* Class for handling async print progress callback */ + public class gsPrintEventArgs : EventArgs + { + private String m_status; + private bool m_completed; + private int m_page; + + public String Status + { + get { return m_status; } + } + + public bool Completed + { + get { return m_completed; } + } + + public int Page + { + get { return m_page; } + } + + public gsPrintEventArgs(String status, bool completed, int page) + { + m_completed = completed; + m_status = status; + m_page = page; + } + } + + public class gsprint + { + private XpsDocumentWriter m_docWriter = null; + internal delegate void AsyncPrintCallBack(object printObject, gsPrintEventArgs info); + internal event AsyncPrintCallBack PrintUpdate; + + /* Show std. print dialog */ + public PrintDialog GetPrintDialog() + { + PrintDialog dlg = new PrintDialog(); + /* Current page and page ranges is going to require a little work */ + dlg.PageRangeSelection = PageRangeSelection.AllPages; + //dlg.UserPageRangeEnabled = true; + //dlg.CurrentPageEnabled = true; + dlg.SelectedPagesEnabled = false; + if (dlg.ShowDialog() == true) + return dlg; + return null; + } + + /* Main print entry point */ + public void Print(PrintQueue queu, FixedDocumentSequence fixdoc) + { + XpsDocumentWriter docwrite = GetDocWriter(queu); + + docwrite.WritingPrintTicketRequired += + new WritingPrintTicketRequiredEventHandler(PrintTicket); + PrintPages(docwrite, fixdoc); + } + + /* Send it */ + private void PrintPages(XpsDocumentWriter xpsdw, FixedDocumentSequence fixdoc) + { + m_docWriter = xpsdw; + xpsdw.WritingCompleted += + new WritingCompletedEventHandler(AsyncCompleted); + xpsdw.WritingProgressChanged += + new WritingProgressChangedEventHandler(AsyncProgress); + xpsdw.WriteAsync(fixdoc); + } + + public void CancelAsync() + { + /* ick. This does not work in windows 8. causes crash */ + /* https://connect.microsoft.com/VisualStudio/feedback/details/778145/xpsdocumentwriter-cancelasync-cause-crash-in-win8 */ + m_docWriter.CancelAsync(); + } + + /* Done */ + private void AsyncCompleted(object sender, WritingCompletedEventArgs e) + { + string status = null; + if (e.Cancelled) + status = "Print Canceled"; + else if (e.Error != null) + status = "Print Error"; + else + status = "Print Completed"; + + if (PrintUpdate != null) + { + gsPrintEventArgs info = new gsPrintEventArgs(status, true, 0); + PrintUpdate(this, info); + } + } + + /* Do this update with each fixed document (page) that is handled */ + private void AsyncProgress(object sender, WritingProgressChangedEventArgs e) + { + if (PrintUpdate != null) + { + String progress = "Page " + e.Number; + gsPrintEventArgs info = new gsPrintEventArgs(progress, false, e.Number); + PrintUpdate(this, info); + } + } + + /* Print ticket handling. You can customize for PrintTicketLevel at + FixedDocumentSequencePrintTicket, FixedDocumentPrintTicket, + or FixedPagePrintTicket. We may want to play around with this some */ + private void PrintTicket(Object sender, WritingPrintTicketRequiredEventArgs e) + { + if (e.CurrentPrintTicketLevel == + PrintTicketLevel.FixedDocumentSequencePrintTicket) + { + PrintTicket pts = new PrintTicket(); + pts.PageOrientation = PageOrientation.Portrait; + e.CurrentPrintTicket = pts; + } + } + + /* Create the document write */ + private XpsDocumentWriter GetDocWriter(PrintQueue pq) + { + XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(pq); + return xpsdw; + } + } +} diff --git a/platform/winrt/gsview/gsview.csproj b/platform/winrt/gsview/gsview.csproj index 961eab19..6856e925 100644 --- a/platform/winrt/gsview/gsview.csproj +++ b/platform/winrt/gsview/gsview.csproj @@ -13,6 +13,7 @@ 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 + false publish\ true Disk @@ -25,12 +26,11 @@ true 0 1.0.0.%2a - false false true - AnyCPU + x64 true full false @@ -38,9 +38,11 @@ TRACE;DEBUG prompt 4 + MinimumRecommendedRules.ruleset + true - AnyCPU + x64 pdbonly true bin\Release\ @@ -48,19 +50,25 @@ prompt 4 + + + + + gsview.ico + + + - - - - 4.0 + + @@ -71,8 +79,28 @@ Designer + + + Convert.xaml + + + + + gsOutput.xaml + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + MSBuild:Compile Designer @@ -138,6 +166,31 @@ false + + + + + + + + + + + + + + + + + + + + + + + + +