-
Notifications
You must be signed in to change notification settings - Fork 176
Expand file tree
/
Copy pathTempReload.cs
More file actions
137 lines (128 loc) · 6.65 KB
/
Copy pathTempReload.cs
File metadata and controls
137 lines (128 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Docnet.Core;
using Docnet.Core.Models;
using Microsoft.Win32;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using KillerPDF.Services;
using PdfPigDoc = UglyToad.PdfPig.PdfDocument;
namespace KillerPDF
{
public partial class MainWindow
{
// ============================================================
// Temp save/reload
// ============================================================
private void SaveTempAndReload(bool keepAnnotations = false, bool preserveZoom = false)
{
if (_doc is null || _currentFile is null) return;
// Overlay annotations are unsaved, still-editable user work. Callers that don't change
// page identity (crop) pass keepAnnotations:true so annotations on other pages survive
// the reload and stay selectable/movable; they are re-rendered after the doc reopens.
if (!keepAnnotations) _annotations.Clear();
_renderDims.Clear();
InvalidateRenderCache(_active); // pages changed pixels / order: drop this tab's cached bitmaps
_renderedPrimaryPage = -1; // force a re-render after reload even if the same page stays selected (e.g. rotate)
ClearSelection();
MarkDirty();
var doc = _doc;
int selectedIdx = PageList.SelectedIndex;
// Capture page rotations, then strip them from the document before saving.
// Docnet uses FPDF_GetPageWidth/Height (MediaBox, no rotation) to size the bitmap,
// then renders with PDFium's page CTM which *does* include /Rotate. For 90�/270�
// the rendered landscape content overflows the portrait-sized bitmap and gets clipped.
// Stripping /Rotate to 0 before saving means Docnet renders clean unrotated content
// that fits the bitmap; RotateBitmap is applied in each render path instead.
_pageRotations.Clear();
for (int i = 0; i < doc.PageCount; i++)
{
int rot = ((doc.Pages[i].Rotate % 360) + 360) % 360;
_pageRotations[i] = rot;
doc.Pages[i].Rotate = 0;
}
var tempPath = App.MakeTempFile("temp");
try
{
doc.Save(tempPath);
doc.Close();
}
catch (Exception saveEx) when (IsXRefException(saveEx))
{
// PdfSharpCore fails to re-save encrypted PDFs (e.g. owner-restricted RC4 files)
// because it encounters cross-reference tokens while serialising dirty objects.
// Primary fallback: use PDFium (already initialised for the page preview) to
// load the source, strip all /Rotate values, remove encryption, and save.
// Secondary fallback: PdfSharpCore Import mode (works on some non-encrypted xref
// issues but fails on encrypted files; kept as a last resort).
doc.Close();
_doc = null;
if (!TryPdfiumSaveWithZeroRotations(_currentFile!, tempPath) &&
!TryImportRepairToPath(_currentFile!, tempPath, stripRotations: true))
throw; // re-throw original if both fallbacks fail
}
// PdfSharpCore sometimes saves a file where one object's xref offset points at the
// xref table itself (object N offset = xref table position). When PdfSharp then tries
// to re-open that file in Modify mode it seeks to the xref table, reads the keyword
// "xref" as a token in an object context, and throws "Unexpected token 'xref'".
// Fix: catch the reopen failure, pipe the saved file through PDFium (which has
// robust error recovery and will rewrite a correct xref), then retry the open.
try
{
_doc = PdfReader.Open(tempPath, PdfDocumentOpenMode.Modify);
}
catch (Exception openEx) when (IsXRefException(openEx))
{
var fixedPath = App.MakeTempFile("fixed");
if (!TryPdfiumSaveWithZeroRotations(tempPath, fixedPath))
throw; // PDFium also failed - re-throw original reopen error
tempPath = fixedPath;
_doc = PdfReader.Open(tempPath, PdfDocumentOpenMode.Modify);
}
_currentFile = tempPath;
// Restore rotations in the reopened in-memory doc so saves, form fields,
// and all other operations see the correct rotation values.
foreach (var kv in _pageRotations)
_doc.Pages[kv.Key].Rotate = kv.Value;
RefreshPageList();
if (selectedIdx >= 0 && selectedIdx < PageList.Items.Count)
PageList.SelectedIndex = selectedIdx;
else if (PageList.Items.Count > 0)
PageList.SelectedIndex = 0;
// In Continuous view the strip caches one rendered slot per page. After a
// page-modifying reload (e.g. crop) it must be rebuilt so the main view reflects the
// new pages; the slot-sizing in RenderContinuousPages makes cropped pages fit cleanly.
if (_viewMode == ViewMode.Continuous)
{
int contIdx = PageList.SelectedIndex;
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded,
(Action)(() => SetupContinuousView(contIdx)));
return;
}
// Refit synchronously so the first rendered frame uses the correct zoom. For crop we instead
// keep the current zoom (preserveZoom) so the page doesn't jump to fit the smaller cropped size -
// the user just wanted the cropped-away area removed, not a zoom change.
PagePreviewPanel.ScrollToHorizontalOffset(0);
if (preserveZoom) { _fitMode = FitMode.None; ApplyZoom(); }
else ReapplyGridOrFit();
// Deferred refit after layout settles for accurate ActualWidth.
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)(() =>
{
PagePreviewPanel.ScrollToHorizontalOffset(0);
if (preserveZoom) ApplyZoom();
else ReapplyGridOrFit();
}));
}
}
}