using Comal.Classes; using InABox.Clients; using InABox.Core; using Syncfusion.Pdf.Graphics; using Syncfusion.Pdf.Parsing; using Syncfusion.Pdf; using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using InABox.DynamicGrid; using InABox.WPF; using Encoder = System.Drawing.Imaging.Encoder; using Path = System.IO.Path; using Image = System.Windows.Controls.Image; using System.ComponentModel; using System.Windows.Controls; using Clipboard = System.Windows.Clipboard; using DataFormats = System.Windows.DataFormats; using DragDropEffects = System.Windows.DragDropEffects; using DragEventArgs = System.Windows.DragEventArgs; using MessageBox = System.Windows.MessageBox; using UserControl = System.Windows.Controls.UserControl; using InABox.Wpf; using System.Collections.ObjectModel; using System.Windows.Data; using PRSDesktop.Panels.DataEntry; using InABox.Wpf.Editors; using System.Timers; using Microsoft.Win32; using javax.xml.crypto; namespace PRSDesktop; public static class PDFExtensions { public static IEnumerable GetPages(this PdfDocumentBase doc) { if (doc is PdfLoadedDocument lDoc) return lDoc.Pages.Cast(); if (doc is PdfDocument pdfDoc) return pdfDoc.Pages.Cast(); throw new Exception($"Unsupported PDF Document type {doc.GetType()}"); } public static PdfPageBase GetPage(this PdfDocumentBase doc, int index) { if (doc is PdfLoadedDocument lDoc) return lDoc.Pages[index]; if (doc is PdfDocument pdfDoc) return pdfDoc.Pages[index]; throw new Exception($"Unsupported PDF Document type {doc.GetType()}"); } public static int PageCount(this PdfDocumentBase doc) { if (doc is PdfLoadedDocument lDoc) return lDoc.Pages.Count; if (doc is PdfDocument pdfDoc) return pdfDoc.Pages.Count; throw new Exception($"Unsupported PDF Document type {doc.GetType()}"); } public static PdfLoadedDocument AsLoadedDocument(this PdfDocumentBase doc) { if (doc is PdfLoadedDocument lDoc) return lDoc; if (doc is PdfDocument pdfDoc) { using var ms = new MemoryStream(); pdfDoc.Save(ms); var array = ms.ToArray(); return new PdfLoadedDocument(array); } throw new Exception($"Unsupported PDF Document type {doc.GetType()}"); } public static byte[] SaveToBytes(this PdfDocumentBase doc) { using var ms = new MemoryStream(); doc.Save(ms); return ms.ToArray(); } } /// /// Interaction logic for ScanPanel.xaml /// public partial class DataEntryList : UserControl, ICorePanel, IDockPanel { private List SelectedScans = new(); public delegate void DateEntrySelectionHandler(String appliesTo, Guid entityID, bool allowprocess); public event DateEntrySelectionHandler? SelectionChanged; private readonly object _viewListLock = new object(); private class ViewDocument { public ImageSource Image { get; set; } public DataEntryDocument Document { get; set; } public int PageNumber { get; set; } public ViewDocument(ImageSource image, DataEntryDocument document, int page) { Image = image; Document = document; PageNumber = page; } } private List ViewDocuments { get; } = new(); public ObservableCollection ViewList { get; init; } = new(); private List OpenWindows = new(); public DataEntryList() { BindingOperations.EnableCollectionSynchronization(ViewList, _viewListLock); InitializeComponent(); } #region Panel public void Setup() { _dataEntryGrid.HiddenColumns.Add(x => x.Document.ID); _dataEntryGrid.Refresh(true, false); _historyGrid.Refresh(true, false); } public void Refresh() { if (_pages.SelectedIndex == 0) _dataEntryGrid.Refresh(false, true); else if (_pages.SelectedIndex == 1) _historyGrid.Refresh(false,true); } public void Shutdown(CancelEventArgs? cancel) { CloseImageWindows(); } #endregion #region View List private static List RenderTextFile(string textData) { var pdfDocument = new PdfDocument(); var page = pdfDocument.Pages.Add(); var font = new PdfStandardFont(PdfFontFamily.Courier, 14); var textElement = new PdfTextElement(textData, font); var layoutFormat = new PdfLayoutFormat { Layout = PdfLayoutType.Paginate, Break = PdfLayoutBreakType.FitPage }; textElement.Draw(page, new RectangleF(0, 0, page.GetClientSize().Width, page.GetClientSize().Height), layoutFormat); using var docStream = new MemoryStream(); pdfDocument.Save(docStream); var loadeddoc = new PdfLoadedDocument(docStream.ToArray()); Bitmap[] bmpImages = loadeddoc.ExportAsImage(0, loadeddoc.Pages.Count - 1); var jpgEncoder = ImageUtils.GetEncoder(ImageFormat.Jpeg)!; var quality = Encoder.Quality; var encodeParams = new EncoderParameters(1); encodeParams.Param[0] = new EncoderParameter(quality, 100L); var images = new List(); if (bmpImages != null) foreach (var image in bmpImages) { using var data = new MemoryStream(); image.Save(data, jpgEncoder, encodeParams); images.Add(data.ToArray()); } return images; } private void UpdateViewList(bool force = false) { var selected = _dataEntryGrid.SelectedRows.Select(x => x.ToObject()).ToList(); if (!force && selected.Count == SelectedScans.Count && !selected.Any(x => SelectedScans.All(y => x.ID != y.ID))) return; SelectedScans = selected; ViewList.Clear(); ViewDocuments.Clear(); Task.Run(() => { var docs = DataEntryCache.Cache.LoadDocuments(SelectedScans.Select(x => x.Document.ID).Distinct(), checkTimestamp: true); LoadDocuments(docs); }).ContinueWith((task) => { if(task.Exception is not null) { MessageWindow.ShowError("An error occurred while loading the documents", task.Exception); } }, TaskScheduler.FromCurrentSynchronizationContext()); } private void LoadDocuments(IEnumerable documents) { var bitmaps = new Dictionary>(); foreach (var document in documents) { List images; var bitmapImages = new List(); var extension = Path.GetExtension(document.FileName).ToLower(); if (extension == ".pdf") { images = new List(); try { bitmapImages = ImageUtils.RenderPDFToImageSources(document.Data); } catch (Exception e) { MessageBox.Show($"Cannot load document '{document.FileName}': {e.Message}"); } } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp") { images = new List { document.Data }; } else { images = ImageUtils.RenderTextFileToImages(Encoding.UTF8.GetString(document.Data)); } bitmapImages.AddRange(images.Select(x => { try { return ImageUtils.LoadImage(x); } catch (Exception e) { Dispatcher.BeginInvoke(() => { MessageWindow.ShowError($"Cannot load document '{document.FileName}", e); }); } return null; }).Where(x => x != null).Cast()); foreach (var image in bitmapImages) { if (!bitmaps.TryGetValue(document.ID, out var list)) { list = new List(); bitmaps[document.ID] = list; } list.Add(image); } } ViewDocuments.Clear(); var maxWidth = 0.0; foreach (var scan in SelectedScans) { if (bitmaps.TryGetValue(scan.Document.ID, out var list)) { int page = 1; foreach (var bitmap in list) { maxWidth = Math.Max(maxWidth, bitmap.Width); ViewDocuments.Add(new(bitmap, scan, page)); page++; } } } lock (_viewListLock) { ViewList.Clear(); foreach(var doc in ViewDocuments) { ViewList.Add(doc.Image); } if(maxWidth != 0.0) { ZoomPanel.Scale = ZoomPanel.ActualWidth / (maxWidth * 1.1); ZoomPanel.MinScale = ZoomPanel.Scale / 2; } } } #endregion #region Uploading private static byte[] RenderData(ref string filename, byte[] data) { var extension = Path.GetExtension(filename).ToLower(); if ((extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp") && ImageUtils.TryGetImageType(data, out var format)) { return data; } else if (extension == ".pdf") { return data; } else { using var stream = new MemoryStream(data); filename = Path.ChangeExtension(filename, "pdf"); return DataEntryReGroupWindow.RenderToPDF(filename, stream).SaveToBytes(); } throw new Exception("Could not render file to PDF"); } private void DynamicTabItem_Drop(object sender, DragEventArgs e) { Task.Run(() => { Dispatcher.Invoke(() => { Progress.Show("Uploading documents"); try { var result = DocumentUtils.HandleFileDrop(e); if (result is not null) { foreach (var (filename, stream) in result) { var newFilename = filename; byte[] data; if (stream is null) { data = File.ReadAllBytes(newFilename); } else { using var memStream = new MemoryStream(); stream.CopyTo(memStream); data = memStream.ToArray(); } data = RenderData(ref newFilename, data); _dataEntryGrid.UploadDocument(newFilename, data, Guid.Empty); } } Progress.Close(); } catch (Exception e) { Progress.Close(); MessageWindow.ShowError("Could not upload documents.", e); } }); }); } private void DynamicTabItem_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor")) { e.Effects = DragDropEffects.Copy; } else { e.Effects = DragDropEffects.None; } e.Handled = true; } #endregion private void _documents_OnSelectItem(object sender, DynamicGridSelectionEventArgs e) { UpdateViewList(false); DoSelect(e.Rows); } private void DoSelect(CoreRow[]? rows) { var appliesTo = rows?.Length == 1 ? rows[0].Get(x => x.Tag.AppliesTo) : ""; var entityid = rows?.Length == 1 ? rows[0].Get(x => x.EntityID) : Guid.Empty; var archived = rows?.Length == 1 ? rows[0].Get(x => x.Archived) : DateTime.MinValue; SelectionChanged?.Invoke(appliesTo, entityid, archived.IsEmpty()); CloseImageWindows(); } private void _historyGrid_OnOnSelectItem(object sender, DynamicGridSelectionEventArgs e) { DoSelect(e.Rows); } private void CloseImageWindows() { while (OpenWindows.Count > 0) { var win = OpenWindows.Last(); OpenWindows.RemoveAt(OpenWindows.Count - 1); win.Close(); } } private void OpenImageWindow(ImageSource image) { var window = OpenWindows.FirstOrDefault(x => x.Images.Contains(image)); if (window is not null) { window.Activate(); } else { window = new DataEntryDocumentWindow(); window.Topmost = true; window.Images.Add(image); OpenWindows.Add(window); window.Closed += OpenWindow_Closed; window.Show(); } } private void Image_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (sender is not Image image) return; if(e.ClickCount >= 2) { OpenImageWindow(image.Source); e.Handled = true; } } private void OpenWindow_Closed(object? sender, EventArgs e) { if (sender is not DataEntryDocumentWindow window) return; OpenWindows.Remove(window); } private void _Explode_OnClick(object sender, RoutedEventArgs e) { _dataEntryGrid.DoExplode(); } private List? _tags; private void _uploadMenu_OnOpened(object sender, RoutedEventArgs e) { if (Clipboard.ContainsText()) { _pasteItem.Header = "Paste Text from Clipboard"; _pasteItem.Visibility = Visibility.Visible; } else if (Clipboard.ContainsImage()) { _pasteItem.Header = "Paste Image from Clipboard"; _pasteItem.Visibility = Visibility.Visible; } else if (Clipboard.ContainsFileDropList()) { int count = CheckAllowableFiles(); if (count > 0) { _pasteItem.Header = $@"Paste {count} File{(count > 1 ? "s" : "")} from Clipboard"; _pasteItem.Visibility = Visibility.Visible; } } else _pasteItem.Visibility = Visibility.Collapsed; _archive.Visibility = _dataEntryGrid.SelectedRows.Any() && Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; _archiveseparator.Visibility = _archive.Visibility; _tags ??= DataEntryGrid.GetVisibleTagList(); _changeTag.Items.Clear(); foreach (var tag in _tags) _changeTag.Items.Add(new MenuItem() { Header = tag.Name, Command = new Command((_) => ChangeTag(tag)) { } }); _changeTag.Items.Add(new Separator()); _changeTag.Items.Add(new MenuItem() { Header= "Clear Tags", Command = new Command((_) => ChangeTag(new DataEntryTag())) }); _changeTag.Visibility = _dataEntryGrid.SelectedRows.Any() && _tags.Any() && (Security.CanEdit() || Security.IsAllowed()) ? Visibility.Visible : Visibility.Collapsed; _changeNote.Visibility = _dataEntryGrid.SelectedRows.Any() && Security.CanEdit() ? Visibility.Visible : Visibility.Collapsed; _changetagseparator.Visibility = _archive.Visibility; } private void ChangeTag(object obj) { if (obj is DataEntryTag tag) _dataEntryGrid.DoChangeTags(tag.ID); } private void _addItem_OnClick(object sender, RoutedEventArgs e) { var ofd = new OpenFileDialog() { Filter = @"All Files (*.pdf, *.bmp, *.png, *.jpg, *.jpeg)|*.pdf;*.bmp;*.png;*.jpg;*.jpeg", Multiselect = true, Title = @"Select Files to Upload", InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) }; if (ofd.ShowDialog() == true) { foreach (var file in ofd.FileNames) Upload( Path.GetFileName(file), new FileStream(file,FileMode.Open)); } } private void _pasteItem_OnClick(object sender, RoutedEventArgs e) { if (Clipboard.ContainsText()) { Upload( $"Pasted Text {DateTime.Now:yyyy-MM-dd hh-mm-ss-fff}.txt", new MemoryStream(new UTF8Encoding().GetBytes(Clipboard.GetText())) ); } else if (Clipboard.ContainsImage()) { var img = Clipboard.GetImage(); if (img != null) { var bmp = ImageUtils.BitmapSourceToBitmap(img); using (var ms = new MemoryStream()) { bmp.Save(ms, ImageFormat.Png); Upload( $"Pasted Image {DateTime.Now:yyyy-MM-dd hh-mm-ss-fff}.png", ms ); } } } else if (CheckAllowableFiles() > 0) { var files = Clipboard.GetFileDropList().OfType().ToArray(); foreach (var file in files) { Upload( Path.GetFileName(file), new FileStream(file,FileMode.Open) ); } } } private void Upload(string filename, Stream data) { var doc = DataEntryReGroupWindow.RenderToPDF(filename, data); _dataEntryGrid.UploadDocument(Path.ChangeExtension(filename,"pdf"), doc.SaveToBytes(), Guid.Empty); } private static int CheckAllowableFiles() { var extensions = Clipboard.GetFileDropList().OfType().Select(x => Path.GetExtension(x.ToUpper())).ToArray(); return extensions.Count(x => String.Equals(x, "PDF") || String.Equals(x, "PNG") || String.Equals(x, "JPG") || String.Equals(x, "JPEG") || String.Equals(x, "BMP") || String.Equals(x, "TXT") ); } private void _pages_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_pages.SelectedIndex == 0) _dataEntryGrid.Refresh(false,true); else if (_pages.SelectedIndex == 1) _historyGrid.Refresh(false,true); } private void _remove_OnClick(object sender, RoutedEventArgs e) { _dataEntryGrid.DoRemove(); } private void _reopen_OnClick(object sender, RoutedEventArgs e) { _historyGrid.DoReopen(); } private void _changeNote_Click(object sender, RoutedEventArgs e) { var notes = _dataEntryGrid.SelectedRows.ToObjects().Select(x => x.Note).Distinct().ToArray(); var note = notes.Length == 1 ? notes[0] : ""; if(TextBoxDialog.Execute("Enter note:", ref note)) { _dataEntryGrid.DoChangeNote(note); } } private void _dataEntryGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e) { } private void _ShowImage_Click(object sender, RoutedEventArgs e) { if (sender is not MenuItem item || item.Tag is not ImageSource image) return; OpenImageWindow(image); } private void RotateDocument(Document doc, int pageNumber) { var extension = Path.GetExtension(doc.FileName).ToLower(); if (extension == ".pdf") { var loadeddoc = new PdfLoadedDocument(doc.Data); bool allPages = loadeddoc.PageCount() > 1; if (allPages) { allPages = MessageWindow.New() .Message("Do you want to rotate all pages in this PDF?") .Title("Rotate all?") .AddYesButton("All pages") .AddNoButton("Just this page") .Display().Result == MessageWindowResult.Yes; } if(allPages) { foreach (var page in loadeddoc.GetPages()) { var rotation = (int)page.Rotation; rotation = (rotation + 1) % 4; page.Rotation = (PdfPageRotateAngle)rotation; } } else if(pageNumber <= loadeddoc.PageCount()) { var page = loadeddoc.GetPage(pageNumber - 1); var rotation = (int)page.Rotation; rotation = (rotation + 1) % 4; page.Rotation = (PdfPageRotateAngle)rotation; } doc.Data = loadeddoc.SaveToBytes(); } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp") { using var stream = new MemoryStream(doc.Data); var bitmap = Bitmap.FromStream(stream); bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); using var outStream = new MemoryStream(); bitmap.Save(outStream, extension switch { ".jpg" or ".jpeg" => ImageFormat.Jpeg, ".png" => ImageFormat.Png, _ => ImageFormat.Bmp }); doc.Data = outStream.ToArray(); } else { using var stream = new MemoryStream(doc.Data); var loadeddoc = DataEntryReGroupWindow.RenderToPDF(doc.FileName, stream); foreach (var page in loadeddoc.GetPages()) { var rotation = (int)page.Rotation; rotation = (rotation + 1) % 4; page.Rotation = (PdfPageRotateAngle)rotation; } doc.Data = loadeddoc.SaveToBytes(); } } private void _RotateImage_Click(object sender, RoutedEventArgs e) { if (sender is not MenuItem item || item.Tag is not ImageSource image) return; var document = ViewDocuments.FirstOrDefault(x => x.Image == image); if (document is null) { MessageWindow.ShowError("An error occurred", "Document does not exist in ViewDocuments list"); return; } var doc = DataEntryCache.Cache.LoadDocuments(CoreUtils.One(document.Document.Document.ID), checkTimestamp: true).First(); try { RotateDocument(doc, document.PageNumber); } catch(Exception err) { MessageWindow.ShowError("Something went wrong while trying to rotate this document.", err); return; } Client.Save(doc, "Rotated by user."); if (Path.GetExtension(doc.FileName) == ".pdf") { document.Document.Thumbnail = ImageUtils.GetPDFThumbnail(doc.Data, 256, 256); } new Client().Save(document.Document, ""); DataEntryCache.Cache.Add(new DataEntryCachedDocument(doc)); UpdateViewList(true); } }