public static async Task <InkingManager> InitializeInking(StorageFolder dataFolder, PdfModel pdfModel) { InkingManager inkManager = new InkingManager(dataFolder); inkManager.inAppInking = await InkingInApp.InitializeInking(dataFolder); inkManager.pdfModel = pdfModel; return(inkManager); }
public static async Task<InkingManager> InitializeInking(StorageFolder dataFolder) { InkingManager inkManager = new InkingManager(dataFolder); inkManager.inAppInking = await InAppInking.InitializeInking(dataFolder); foreach (KeyValuePair<int, InkStrokeContainer> entry in inkManager.InkDictionary) { inkManager.inAppInkStrokes[entry.Key] = new List<InkStroke>(entry.Value.GetStrokes()); } // TODO: Initialize in-file strokes return inkManager; }
public async Task<bool> SaveInkingToPdf(InkingManager inkManager) { bool status = await sfPdf.SaveInkingToPdf(inkManager, msPdf.PdfDoc); return status; }
/// <summary> /// Save the ink annotations into the pdf file. /// </summary> /// <param name="inkDictionary"></param> /// <returns></returns> /// <remarks> /// The page size returned from Syncfusion pdf is the media box size. /// The page size displayed to the end user is the crop box size. /// The size of the ink canvas is the same as the crop box size. /// Syncfusion uses the bottom left corner as the origin, while ink canvas uses the top left corner. /// </remarks> public async Task <bool> SaveInkingToPdf(InkingManager inkManager) { // Indicate whether any ink annotation is added to the PDF file bool fileChanged = false; // Remove ereased ink annotations foreach (KeyValuePair <int, List <InkStroke> > entry in await inkManager.ErasedStrokesDictionary()) { // The key of the dictionary is page number, which is 1-based. int pageNumber = entry.Key; PdfLoadedPage sfPage = sfPdf.GetPage(pageNumber); // Get page information from MS model Windows.Data.Pdf.PdfPage msPage = msPdf.GetPage(pageNumber); PageMapping mapping = new PageMapping(msPage, sfPage); List <PdfInkAnnotation> erasedAnnotations = new List <PdfInkAnnotation>(); // Add each ink stroke to the page foreach (InkStroke stroke in entry.Value) { PdfInkAnnotation inkAnnotation = mapping.InkStroke2InkAnnotation(stroke); erasedAnnotations.Add(inkAnnotation); } if (sfPdf.RemoveInkAnnotations(sfPage, erasedAnnotations)) { fileChanged = true; } } // Add new ink annotations foreach (KeyValuePair <int, InkStrokeContainer> entry in await inkManager.InAppInkDictionary()) { PdfLoadedPage sfPage = sfPdf.GetPage(entry.Key); // Get page information from MS model Windows.Data.Pdf.PdfPage msPage = msPdf.GetPage(entry.Key); PageMapping mapping = new PageMapping(msPage, sfPage); // Add each ink stroke to the page foreach (InkStroke stroke in entry.Value.GetStrokes()) { PdfInkAnnotation inkAnnotation = mapping.InkStroke2InkAnnotation(stroke); sfPage.Annotations.Add(inkAnnotation); fileChanged = true; } } // Save the file only if there are changes. bool inkSaved = false; if (fileChanged) { try { inkSaved = await sfPdf.SaveAsync(); // Copy and replace the actual file await sfFile.CopyAndReplaceAsync(pdfFile); } catch (Exception ex) { // Try to save the file by extracting the pages. StorageFile newFile = await backupFolder.CreateFileAsync("COPY_" + pdfFile.Name, CreationCollisionOption.GenerateUniqueName); try { await sfPdf.CopyPages(newFile); inkSaved = true; // Copy and replace the actual file await newFile.CopyAndReplaceAsync(pdfFile); } catch { App.NotifyUser(typeof(ViewerPage), "Error: \n" + ex.Message, true); } } } return(!(inkSaved ^ fileChanged)); }
/// <summary> /// Save the ink annotations into the pdf file. /// </summary> /// <param name="inkManager"></param> /// <returns></returns> /// <remarks> /// The page size returned from Syncfusion pdf is the media box size. /// The page size displayed to the end user is the crop box size. /// The size of the ink canvas is the same as the crop box size. /// Syncfusion uses the bottom left corner as the origin, while ink canvas uses the top left corner. /// </remarks> public async Task<bool> SaveInkingToPdf(InkingManager inkManager, Windows.Data.Pdf.PdfDocument pdfDoc) { // Indicate whether any ink annotation is added to the PDF file bool fileChanged = false; // Add ink annotations for each page foreach (KeyValuePair<int, InkStrokeContainer> entry in inkManager.InkDictionary) { // The key of the dictionary is page number, which is 1-based. Page index is 0-based. int pageIndex = entry.Key - 1; PdfLoadedPage sfPage = pdf.Pages[pageIndex] as PdfLoadedPage; // Get page information from MS model Windows.Data.Pdf.PdfPage msPage = pdfDoc.GetPage((uint)pageIndex); int rotation = (int)msPage.Rotation; // The page size returned from Syncfusion pdf is the media box size. double scaleRatio = sfPage.Size.Width / msPage.Dimensions.MediaBox.Width; // The ink canvas size is the same as crop box // Crop box could be smaller than media box // There will be an offset if the crop box is smaller than the media box. double xOffset = msPage.Dimensions.CropBox.Left * scaleRatio; double yOffset = msPage.Dimensions.CropBox.Top * scaleRatio; RectangleF rectangle = new RectangleF(0, 0, sfPage.Size.Width, sfPage.Size.Height); // Add each ink stroke to the page foreach (InkStroke stroke in entry.Value.GetStrokes()) { List<float> strokePoints = new List<float>(); foreach (InkPoint p in stroke.GetInkPoints()) { float X = (float)(p.Position.X * scaleRatio + xOffset); float Y = (float)(p.Position.Y * scaleRatio + yOffset); switch (rotation) { case 0: // No rotation { strokePoints.Add(X); strokePoints.Add(sfPage.Size.Height - Y); break; } case 1: // 90-degree rotation { strokePoints.Add(Y); strokePoints.Add(X); break; } case 2: // 180-degree rotation { strokePoints.Add(sfPage.Size.Width - X); strokePoints.Add(Y); break; } case 3: // 270-degree rotation { strokePoints.Add(sfPage.Size.Height - Y); strokePoints.Add(sfPage.Size.Width - X); break; } } } PdfInkAnnotation inkAnnotation = new PdfInkAnnotation(rectangle, strokePoints); // Color inkAnnotation.Color = new PdfColor(fromUIColor(stroke.DrawingAttributes.Color)); // Size inkAnnotation.BorderWidth = (int) Math.Round(stroke.DrawingAttributes.Size.Width * scaleRatio); sfPage.Annotations.Add(inkAnnotation); fileChanged = true; } } bool inkSaved = false; // Save the file only if there are changes. if (fileChanged) { try { inkSaved = await pdf.SaveAsync(pdfFile); } catch (Exception ex) { App.NotifyUser(typeof(ViewerPage), "Error: \n" + ex.Message, true); } } //pdf.Close(true); return !(inkSaved ^ fileChanged); }
/// <summary> /// Converts an ink annotation (from the PDF file) to an ink stroke (to be displayed on the screen). /// </summary> /// <param name="inkAnnotation"></param> /// <param name="mapping"></param> /// <returns></returns> public InkStroke InkAnnotation2InkStroke(PdfLoadedInkAnnotation inkAnnotation) { List <float> strokePoints = inkAnnotation.InkList; InkStrokeBuilder builder = new InkStrokeBuilder(); List <Point> InkPoints = new List <Point>(); // Construct ink points for (int i = 0; i < strokePoints.Count; i = i + 2) { if ((i + 1) >= strokePoints.Count) { // TODO: Something must be wrong. break; } double X = 0, Y = 0; float W = strokePoints[i]; float Z = strokePoints[i + 1]; switch (Rotation) { case 0: // No rotation { X = W; Y = PageSize.Height - Z; break; } case 1: // 90-degree rotation { X = Z; Y = W; break; } case 2: // 180-degree rotation { X = PageSize.Width - W; Y = Z; break; } case 3: // 270-degree rotation { X = PageSize.Width - Z; Y = PageSize.Height - W; break; } } double pointX = (X - Offset.X) / ScaleRatio; double pointY = (Y - Offset.Y) / ScaleRatio; InkPoints.Add(new Point(pointX, pointY)); } InkStroke stroke = builder.CreateStroke(InkPoints); Windows.UI.Color color = ColorToUI(inkAnnotation.Color); double width = inkAnnotation.BorderWidth; if (width < InkingPreference.MIN_PEN_SIZE) { width = InkingPreference.MIN_PEN_SIZE; } width = width / ScaleRatio; Size size = new Size(width, width); if (inkAnnotation.Opacity == InkingManager.HighlighterOpacity) { stroke.DrawingAttributes = InkingManager.HighlighterDrawingAttributes(color, size); } else { stroke.DrawingAttributes = InkingManager.PencilDrawingAttributes(color, size); } return(stroke); }
/// <summary> /// This method is not reliable. /// This method should only be called either right after getting the future access token, /// or at the end of FinishInitialization() after fileLoaded has been set to true, /// depending on the performance and the size of the future access list. /// </summary> /// <returns></returns> private async Task CheckFutureAccessList() { string oldToken = null; AccessListEntryView futureAccessEntries = StorageApplicationPermissions.FutureAccessList.Entries; // If no recent file if (futureAccessEntries.Count == 0) return; else { System.Diagnostics.Stopwatch fileCheckingWatch = new System.Diagnostics.Stopwatch(); fileCheckingWatch.Start(); for (int i = 0; i < futureAccessEntries.Count; i++) { AccessListEntry entry = futureAccessEntries[i]; if (entry.Token == this.futureAccessToken) continue; StorageFile pdfFileInList = null; try { pdfFileInList = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(entry.Token); } catch { // remove the entry if there is an exception StorageApplicationPermissions.FutureAccessList.Remove(entry.Token); } if (pdfFileInList == null) continue; if (this.pdfStorageFile.IsEqual(pdfFileInList)) { oldToken = entry.Token; StorageApplicationPermissions.FutureAccessList.Remove(entry.Token); break; } } fileCheckingWatch.Stop(); AppEventSource.Log.Debug("ViewerPage: Went through future access list in " + fileCheckingWatch.Elapsed.TotalSeconds.ToString() + " seconds"); } if (oldToken != null) { AppEventSource.Log.Info("ViewerPage: File matched existing token in access list. " + oldToken); try { StorageFolder oldDataFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync(oldToken); await oldDataFolder.RenameAsync(this.futureAccessToken, NameCollisionOption.ReplaceExisting); AppEventSource.Log.Info("ViewerPage: Folder " + oldToken + " renamed to " + this.futureAccessToken); if (this.fileLoaded) { this.dataFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync(this.futureAccessToken); this.inkManager = await InkingManager.InitializeInking(dataFolder); RefreshViewer(); } } catch (Exception e) { if (e is FileNotFoundException) { AppEventSource.Log.Warn("ViewerPage: Folder " + oldToken + " not found. "); } else throw new Exception(e.Message); } } }
private async void FinishInitialization() { this.fullScreenCover.Visibility = Visibility.Collapsed; this.imagePanel.UpdateLayout(); // Load viewer state await LoadViewerState(); RestoreViewerState(); // Load inking inkManager = await InkingManager.InitializeInking(dataFolder); // Make sure about the visible page range this._visiblePageRange = FindVisibleRange(); this.fileLoaded = true; this.fileLoadingWatch.Stop(); RefreshViewer(); AppEventSource.Log.Info("ViewerPage: Finished Preparing the file in " + fileLoadingWatch.Elapsed.TotalSeconds.ToString()); this.zoomOutGrid.ItemsSource = pageThumbnails; this.recycleTimer.Start(); }