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();
}
}
}