using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.ComponentModel; using System.Diagnostics; using Microsoft.Win32; using GhostNET; using System.IO; using System.Windows.Media; static class Constants { public const double SCALE_THUMB = 0.1; public const int BLANK_WIDTH = 17; public const int BLANK_HEIGHT = 22; public const int DEFAULT_GS_RES = 300; public const int PAGE_VERT_MARGIN = 10; public const int MAX_PRINT_PREVIEW_LENGTH = 250; public const int ZOOM_MAX = 4; public const double ZOOM_MIN = 0.25; } namespace ghostnet_wpf_example { /// /// Interaction logic for MainWindow.xaml /// /// public enum NotifyType_t { MESS_STATUS, MESS_ERROR }; public enum status_t { S_ISOK, E_FAILURE, E_OUTOFMEM, E_NEEDPASSWORD }; public enum Page_Content_t { FULL_RESOLUTION = 0, THUMBNAIL, OLD_RESOLUTION, LOW_RESOLUTION, NOTSET, BLANK }; public enum zoom_t { NO_ZOOM, ZOOM_IN, ZOOM_OUT } public enum doc_t { UNKNOWN, PDF, PS, PCL, PNG, EPS, JPG, TIF, XPS } public enum ViewerState_t { NO_FILE, OPENING, BUSY_RENDER, DOC_OPEN, DISTILLING, PRINTING, RESIZING } public struct idata_t { public int page_num; public Byte[] bitmap; public int height; public int width; public int raster; public double zoom; } public struct pagesizes_t { public Point size; public double cummulative_y; } public partial class MainWindow : Window { ghostsharp m_ghostscript; doc_t m_document_type; bool m_doc_type_has_page_access; String m_currfile; List m_tempfiles; String m_origfile; int m_currpage; gsOutput m_gsoutput; public int m_numpages; private static Pages m_docPages; private static double m_doczoom; public List m_page_sizes; List m_list_thumb; List m_images_rendered; bool m_validZoom; bool m_aa; List m_toppage_pos; int m_page_progress_count; ViewerState_t m_viewer_state; private static List m_pageType; public void ShowMessage(NotifyType_t type, String Message) { if (type == NotifyType_t.MESS_ERROR) { MessageBox.Show(Message, "Error", MessageBoxButton.OK); } else { MessageBox.Show(Message, "Notice", MessageBoxButton.OK); } } public MainWindow() { InitializeComponent(); this.Closing += new System.ComponentModel.CancelEventHandler(Window_Closing); /* Set up ghostscript calls for progress update */ m_ghostscript = new ghostsharp(); m_ghostscript.gsUpdateMain += new ghostsharp.gsCallBackMain(gsProgress); m_ghostscript.gsIOUpdateMain += new ghostsharp.gsIOCallBackMain(gsIO); m_ghostscript.gsDLLProblemMain += new ghostsharp.gsDLLProblem(gsDLL); m_ghostscript.DisplayDeviceOpen(); m_currpage = 0; m_gsoutput = new gsOutput(); m_gsoutput.Activate(); m_tempfiles = new List(); m_thumbnails = new List(); m_docPages = new Pages(); m_pageType = new List(); m_page_sizes = new List(); m_document_type = doc_t.UNKNOWN; m_doczoom = 1.0; m_viewer_state = ViewerState_t.NO_FILE; m_validZoom = true; m_doc_type_has_page_access = true; m_list_thumb = new List(); m_images_rendered = new List(); m_aa = true; xaml_PageList.AddHandler(Grid.DragOverEvent, new System.Windows.DragEventHandler(Grid_DragOver), true); xaml_PageList.AddHandler(Grid.DropEvent, new System.Windows.DragEventHandler(Grid_Drop), true); /* For case of opening another file, or launching a print process */ string[] arguments = Environment.GetCommandLineArgs(); if (arguments.Length == 3) { string filePath = arguments[1]; string job = arguments[2]; if (String.Equals(job, "open")) ProcessFile(filePath); } else if (arguments.Length == 5) { string filePath = arguments[1]; string job = arguments[2]; try { m_currpage = Int32.Parse(arguments[3]); m_numpages = Int32.Parse(arguments[4]); } catch (FormatException) { Console.WriteLine("Failure to parse print page info"); Close(); } /* Keep main window hidden if we are doing a print process */ this.IsVisibleChanged += new System.Windows.DependencyPropertyChangedEventHandler(WindowVisible); m_viewer_state = ViewerState_t.PRINTING; Print(filePath); } } private void gsIO(String mess, int len) { m_gsoutput.Update(mess, len); } private void ShowGSMessage(object sender, RoutedEventArgs e) { m_gsoutput.Show(); } private void gsDLL(String mess) { ShowMessage(NotifyType_t.MESS_STATUS, mess); } private void gsThumbRendered(int width, int height, int raster, IntPtr data, gsParamState_t state) { ThumbPageCallback(width, height, raster, state.zoom, state.currpage, data); } private void gsPageRendered(int width, int height, int raster, IntPtr data, gsParamState_t state) { MainPageCallback(width, height, raster, state.zoom, state.currpage, data); } private void gsProgress(gsEventArgs asyncInformation) { if (asyncInformation.Completed) { switch (asyncInformation.Params.task) { case GS_Task_t.CREATE_XPS: m_printstatus.xaml_PrintProgress.Value = 100; m_printstatus.xaml_PrintProgressGrid.Visibility = System.Windows.Visibility.Collapsed; break; case GS_Task_t.PS_DISTILL: xaml_DistillProgress.Value = 100; xaml_DistillGrid.Visibility = System.Windows.Visibility.Collapsed; break; case GS_Task_t.SAVE_RESULT: break; case GS_Task_t.DISPLAY_DEV_THUMBS: ThumbsDone(); break; case GS_Task_t.DISPLAY_DEV_RUN_FILE: case GS_Task_t.DISPLAY_DEV_PDF: case GS_Task_t.DISPLAY_DEV_NON_PDF: RenderingDone(); break; } 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: ShowMessage(NotifyType_t.MESS_STATUS, "Ghostscript failed to convert document"); break; default: ShowMessage(NotifyType_t.MESS_STATUS, "Ghostscript failed."); break; } return; } GSResult(asyncInformation.Params); } else { switch (asyncInformation.Params.task) { case GS_Task_t.CREATE_XPS: m_printstatus.xaml_PrintProgress.Value = asyncInformation.Progress; break; case GS_Task_t.PS_DISTILL: this.xaml_DistillProgress.Value = asyncInformation.Progress; break; case GS_Task_t.SAVE_RESULT: break; } } } /* GS Result*/ public void GSResult(gsParamState_t gs_result) { TempFile tempfile = null; if (gs_result.outputfile != null) tempfile = new TempFile(gs_result.outputfile); if (gs_result.result == GS_Result_t.gsCANCELLED) { xaml_DistillGrid.Visibility = System.Windows.Visibility.Collapsed; if (tempfile != null) { try { tempfile.DeleteFile(); } catch { ShowMessage(NotifyType_t.MESS_STATUS, "Problem Deleting Temp File"); } } return; } if (gs_result.result == GS_Result_t.gsFAILED) { xaml_DistillGrid.Visibility = System.Windows.Visibility.Collapsed; ShowMessage(NotifyType_t.MESS_STATUS, "GS Failed Conversion"); if (tempfile != null) { try { tempfile.DeleteFile(); } catch { ShowMessage(NotifyType_t.MESS_STATUS, "Problem Deleting Temp File"); } } return; } switch (gs_result.task) { case GS_Task_t.CREATE_XPS: /* Always do print all from xps conversion as it will do * the page range handling for us */ /* Add file to temp file list */ m_tempfiles.Add(tempfile); PrintXPS(gs_result.outputfile, true, -1, -1, true); break; case GS_Task_t.PS_DISTILL: xaml_DistillGrid.Visibility = System.Windows.Visibility.Collapsed; m_origfile = gs_result.inputfile; /* Save distilled result */ SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "PDF file (*.pdf)|*.pdf"; dlg.FileName = System.IO.Path.GetFileNameWithoutExtension(m_origfile) + ".pdf"; if (dlg.ShowDialog() == true) { try { if (File.Exists(dlg.FileName)) { File.Delete(dlg.FileName); } File.Copy(tempfile.Filename, dlg.FileName); } catch (Exception except) { ShowMessage(NotifyType_t.MESS_ERROR, "Exception Saving Distilled Result:" + except.Message); } } tempfile.DeleteFile(); m_viewer_state = ViewerState_t.NO_FILE; break; case GS_Task_t.SAVE_RESULT: /* Don't delete file in this case as this was our output! */ ShowMessage(NotifyType_t.MESS_STATUS, "GS Completed Conversion"); break; } } private void OpenFileCommand(object sender, ExecutedRoutedEventArgs e) { OpenFile(sender, e); } private void CleanUp() { /* Collapse this stuff since it is going to be released */ xaml_ThumbGrid.Visibility = System.Windows.Visibility.Collapsed; /* Clear out everything */ if (m_docPages != null && m_docPages.Count > 0) m_docPages.Clear(); if (m_pageType != null && m_pageType.Count > 0) m_pageType.Clear(); if (m_thumbnails != null && m_thumbnails.Count > 0) m_thumbnails.Clear(); if (m_toppage_pos != null && m_toppage_pos.Count > 0) m_toppage_pos.Clear(); if (m_list_thumb != null && m_list_thumb.Count > 0) m_list_thumb.Clear(); if (m_images_rendered != null && m_images_rendered.Count > 0) m_images_rendered.Clear(); if (m_page_sizes != null && m_page_sizes.Count > 0) m_page_sizes.Clear(); m_currfile = null; m_origfile = null; m_numpages = -1; m_doc_type_has_page_access = true; m_document_type = doc_t.UNKNOWN; m_origfile = null; CleanUpTempFiles(); xaml_TotalPages.Text = "/ 0"; xaml_currPage.Text = "0"; CloseExtraWindows(false); m_ghostscript.gsPageRenderedMain -= new ghostsharp.gsCallBackPageRenderedMain(gsPageRendered); m_ghostscript.DisplayDeviceClose(); m_ghostscript.DisplayDeviceOpen(); m_viewer_state = ViewerState_t.NO_FILE; /* Set vertical scroll to top position */ Decorator border = VisualTreeHelper.GetChild(xaml_PageList, 0) as Decorator; ScrollViewer scrollViewer = border.Child as ScrollViewer; scrollViewer.ScrollToVerticalOffset(0); return; } private void CloseCommand(object sender, ExecutedRoutedEventArgs e) { if (m_viewer_state == ViewerState_t.DOC_OPEN) CleanUp(); } private bool ReadyForOpen() { /* Check if gs is currently busy. If it is then don't allow a new * file to be opened. They can cancel gs with the cancel button if * they want */ if (m_ghostscript.GetStatus() != gsStatus.GS_READY) { ShowMessage(NotifyType_t.MESS_STATUS, "GS busy. Cancel to open new file."); return false; } return true; } private void OpenFile(object sender, RoutedEventArgs e) { if (!ReadyForOpen()) return; OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Document Files(*.ps;*.eps;*.pdf;*.bin;*.xps;*.oxps;*.jpg;*.png;*.tif|*.ps;*.eps;*.pdf;*.bin;*.xps;*.oxps;*.jpg;*.png;*.tif|All files (*.*)|*.*"; dlg.FilterIndex = 1; if (dlg.ShowDialog() == true) ProcessFile(dlg.FileName); } public void ProcessFile(String FileName) { /* Before we even get started check for issues */ /* First check if file exists and is available */ if (!System.IO.File.Exists(FileName)) { ShowMessage(NotifyType_t.MESS_STATUS, "File not found!"); return; } if (m_viewer_state == ViewerState_t.DOC_OPEN) { /* In this case, we want to go ahead and launch a new process * handing it the filename */ /* We need to get the location */ string path = System.Reflection.Assembly.GetExecutingAssembly().CodeBase; try { string Arguments = FileName + " open"; Process.Start(path, Arguments); } catch (InvalidOperationException) { Console.WriteLine("InvalidOperationException"); } catch (Win32Exception) { Console.WriteLine("Win32 Exception: There was an error in opening the associated file. "); } return; } else if (m_viewer_state != ViewerState_t.NO_FILE) return; m_viewer_state = ViewerState_t.OPENING; /* If we have a ps or eps file then launch the distiller first * and then we will get a temp pdf file which we will open. This is done * to demo both methods of doing callbacks from gs worker thread. Either * progress as we distill the stream for PS or page by page for PDF */ string extension = System.IO.Path.GetExtension(FileName); /* We are doing this based on the extension but like should do * it based upon the content */ switch (extension.ToUpper()) { case ".PS": m_document_type = doc_t.PS; break; case ".EPS": m_document_type = doc_t.EPS; break; case ".PDF": m_document_type = doc_t.PDF; break; case ".XPS": m_document_type = doc_t.XPS; break; case ".OXPS": m_document_type = doc_t.XPS; break; case ".BIN": m_document_type = doc_t.PCL; break; case ".PNG": m_document_type = doc_t.PNG; break; case ".JPG": m_document_type = doc_t.JPG; break; case ".TIF": m_document_type = doc_t.TIF; break; default: { m_document_type = doc_t.UNKNOWN; ShowMessage(NotifyType_t.MESS_STATUS, "Unknown File Type"); m_viewer_state = ViewerState_t.NO_FILE; return; } } if (m_document_type == doc_t.PCL || m_document_type == doc_t.XPS || m_document_type == doc_t.PS) { MessageBoxResult result = MessageBox.Show("Would you like to distill this file?", "ghostnet", MessageBoxButton.YesNoCancel); switch (result) { case MessageBoxResult.Yes: xaml_DistillProgress.Value = 0; m_viewer_state = ViewerState_t.DISTILLING; if (m_ghostscript.DistillPS(FileName, Constants.DEFAULT_GS_RES) == gsStatus.GS_BUSY) { ShowMessage(NotifyType_t.MESS_STATUS, "GS currently busy"); return; } xaml_DistillName.Text = "Distilling"; xaml_CancelDistill.Visibility = System.Windows.Visibility.Visible; xaml_DistillName.FontWeight = FontWeights.Bold; xaml_DistillGrid.Visibility = System.Windows.Visibility.Visible; return; case MessageBoxResult.No: //m_doc_type_has_page_access = false; break; case MessageBoxResult.Cancel: m_viewer_state = ViewerState_t.NO_FILE; return; } } m_currfile = FileName; RenderThumbs(); return; } private void CancelDistillClick(object sender, RoutedEventArgs e) { } private void DeleteTempFile(String file) { for (int k = 0; k < m_tempfiles.Count; k++) { if (String.Compare(file, m_tempfiles[k].Filename) == 0) { try { m_tempfiles[k].DeleteFile(); m_tempfiles.RemoveAt(k); } catch { ShowMessage(NotifyType_t.MESS_STATUS, "Problem Deleting Temp File"); } break; } } } private void CleanUpTempFiles() { for (int k = 0; k < m_tempfiles.Count; k++) { try { m_tempfiles[k].DeleteFile(); } catch { ShowMessage(NotifyType_t.MESS_STATUS, "Problem Deleting Temp File"); } } m_tempfiles.Clear(); } private void OnAboutClick(object sender, RoutedEventArgs e) { About about = new About(this); var desc_static = about.Description; String desc; String gs_vers = m_ghostscript.GetVersion(); if (gs_vers == null) desc = "\nGhostscript DLL: Not Found"; else desc = "\nGhostscript DLL: Using " + gs_vers + " 64 bit\n"; about.description.Text = desc; about.ShowDialog(); } private static DocPage InitDocPage() { DocPage doc_page = new DocPage(); doc_page.BitMap = null; doc_page.Height = Constants.BLANK_HEIGHT; doc_page.Width = Constants.BLANK_WIDTH; return doc_page; } private void ThumbSelected(object sender, MouseButtonEventArgs e) { var item = ((FrameworkElement)e.OriginalSource).DataContext as DocPage; if (item != null) { if (item.PageNum < 0) return; var obj = xaml_PageList.Items[item.PageNum - 1]; xaml_PageList.ScrollIntoView(obj); } } void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { CloseExtraWindows(true); } void CloseExtraWindows(bool shutdown) { if (shutdown) { if (m_gsoutput != null) m_gsoutput.RealWindowClosing(); if (m_printcontrol != null) m_printcontrol.RealWindowClosing(); } else { if (m_gsoutput != null) m_gsoutput.Hide(); if (m_printcontrol != null) m_printcontrol.Hide(); } } private void PageScrollChanged(object sender, ScrollChangedEventArgs e) { e.Handled = true; if (m_viewer_state != ViewerState_t.DOC_OPEN) return; /* Find the pages that are visible. */ double top_window = e.VerticalOffset; double bottom_window = top_window + e.ViewportHeight; int first_page = -1; int last_page = -1; if (top_window > m_toppage_pos[m_numpages - 1]) { first_page = m_numpages - 1; last_page = first_page; } else { for (int k = 0; k < (m_toppage_pos.Count - 1); k++) { if (top_window <= m_toppage_pos[k + 1] && bottom_window >= m_toppage_pos[k]) { if (first_page == -1) first_page = k; else { last_page = k; break; } } else { if (first_page != -1) { last_page = first_page; break; } } } } m_currpage = first_page; xaml_currPage.Text = (m_currpage + 1).ToString(); if (m_doc_type_has_page_access) PageRangeRender(first_page, last_page); return; } private void Grid_DragOver(object sender, System.Windows.DragEventArgs e) { if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop)) { e.Effects = System.Windows.DragDropEffects.All; } else { e.Effects = System.Windows.DragDropEffects.None; } e.Handled = false; } /* Keep main window hidden if we are doing a print process */ public void WindowVisible(object sender, DependencyPropertyChangedEventArgs e) { if (m_viewer_state == ViewerState_t.PRINTING) { this.Visibility = Visibility.Hidden; } } private void Grid_Drop(object sender, System.Windows.DragEventArgs e) { if (e.Data.GetDataPresent(System.Windows.DataFormats.FileDrop)) { string[] docPath = (string[])e.Data.GetData(System.Windows.DataFormats.FileDrop); ProcessFile(String.Join("", docPath)); } } private void PageEnterClicked(object sender, KeyEventArgs e) { if (e.Key == Key.Return) { e.Handled = true; var desired_page = xaml_currPage.Text; try { int page = System.Convert.ToInt32(desired_page); if (page > 0 && page < (m_numpages + 1)) { var obj = xaml_PageList.Items[page - 1]; xaml_PageList.ScrollIntoView(obj); } } catch (FormatException) { Console.WriteLine("String is not a sequence of digits."); } catch (OverflowException) { Console.WriteLine("The number cannot fit in an Int32."); } } } private void AA_uncheck(object sender, RoutedEventArgs e) { m_aa = false; RenderMain(); } private void AA_check(object sender, RoutedEventArgs e) { m_aa = true; RenderMain(); } } }