-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPolicy.cs
544 lines (503 loc) · 23.9 KB
/
Policy.cs
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using DocumentFormat.OpenXml.Drawing.Pictures;
using DocumentFormat.OpenXml.Vml;
using DocumentFormat.OpenXml.Office2013.ExcelAc;
using ImageMagick;
using DocumentFormat.OpenXml;
namespace Convert.Spreadsheet
{
public class Policy
{
public void OOXML_Errors(string filepath)
{
Remove_DataConnections(filepath);
Remove_CellReferences(filepath);
Remove_RTDFunctions(filepath);
Remove_ExternalObjects(filepath);
Convert_EmbeddedImages(filepath);
}
public void OOXML_Warnings(string filepath)
{
Change_Conformance_ExcelInterop(filepath);
Remove_AbsolutePath(filepath);
Activate_FirstSheet(filepath);
}
// Change conformance to Strict
public void Change_Conformance_ExcelInterop(string filepath)
{
// Open Excel
Excel.Application app = new Excel.Application(); // Create Excel object instance
app.DisplayAlerts = false; // Don't display any Excel prompts
Excel.Workbook wb = app.Workbooks.Open(filepath, ReadOnly: false, Password: "'", WriteResPassword: "'", IgnoreReadOnlyRecommended: true, Notify: false); // Create workbook instance
// Convert to Strict and close Excel
wb.SaveAs(filepath, 61);
wb.Close();
app.Quit();
// If CLISC is run on Windows close Excel in task manager
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Marshal.ReleaseComObject(wb); // Delete workbook task
Marshal.ReleaseComObject(app); // Delete Excel task
}
}
// Remove data connections
public void Remove_DataConnections(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
// Delete all connections
ConnectionsPart conn = spreadsheet.WorkbookPart.ConnectionsPart;
if (conn != null)
{
spreadsheet.WorkbookPart.DeletePart(conn);
Console.WriteLine("Data connection was removed");
}
// Delete all QueryTableParts
IEnumerable<WorksheetPart> worksheetParts = spreadsheet.WorkbookPart.WorksheetParts;
foreach (WorksheetPart worksheetPart in worksheetParts)
{
// Delete all QueryTableParts in WorksheetParts
List<QueryTablePart> queryTables = worksheetPart.QueryTableParts.ToList(); // Must be a list
foreach (QueryTablePart queryTablePart in queryTables)
{
worksheetPart.DeletePart(queryTablePart);
}
// Delete all QueryTableParts, if they are not registered in a WorksheetPart
List<TableDefinitionPart> tableDefinitionParts = worksheetPart.TableDefinitionParts.ToList();
foreach (TableDefinitionPart tableDefinitionPart in tableDefinitionParts)
{
List<IdPartPair> idPartPairs = tableDefinitionPart.Parts.ToList();
foreach (IdPartPair idPartPair in idPartPairs)
{
if (idPartPair.OpenXmlPart.ToString() == "DocumentFormat.OpenXml.Packaging.QueryTablePart")
{
// Delete QueryTablePart
tableDefinitionPart.DeletePart(idPartPair.OpenXmlPart);
// The TableDefinitionPart must also be deleted
worksheetPart.DeletePart(tableDefinitionPart);
// And the reference to the TableDefinitionPart in the WorksheetPart must be deleted
List<TablePart> tableParts = worksheetPart.Worksheet.Descendants<TablePart>().ToList();
foreach (TablePart tablePart in tableParts)
{
if (idPartPair.RelationshipId == tablePart.Id)
{
tablePart.Remove();
}
}
}
}
}
}
// If spreadsheet contains a custom XML Map, delete databinding
if (spreadsheet.WorkbookPart.CustomXmlMappingsPart != null)
{
CustomXmlMappingsPart xmlMap = spreadsheet.WorkbookPart.CustomXmlMappingsPart;
List<Map> maps = xmlMap.MapInfo.Elements<Map>().ToList();
foreach (Map map in maps)
{
if (map.DataBinding != null)
{
map.DataBinding.Remove();
}
}
}
}
}
// Remove RTD functions
public void Remove_RTDFunctions(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
List<WorksheetPart> worksheetparts = spreadsheet.WorkbookPart.WorksheetParts.ToList();
foreach (WorksheetPart part in worksheetparts)
{
Worksheet worksheet = part.Worksheet;
var rows = worksheet.GetFirstChild<SheetData>().Elements<Row>(); // Find all rows
foreach (var row in rows)
{
var cells = row.Elements<Cell>();
foreach (Cell cell in cells)
{
if (cell.CellFormula != null)
{
string formula = cell.CellFormula.InnerText;
if (formula.Length > 2)
{
string hit = formula.Substring(0, 3); // Transfer first 3 characters to string
if (hit == "RTD")
{
CellValue cellvalue = cell.CellValue; // Save current cell value
cell.CellFormula = null; // Remove RTD formula
// If cellvalue does not have a real value
if (cellvalue.Text == "#N/A")
{
cell.DataType = CellValues.String;
cell.CellValue = new CellValue("Invalid data removed");
}
else
{
cell.CellValue = cellvalue; // Insert saved cell value
}
Console.WriteLine("RTD function was removed");
}
}
}
}
}
}
// Delete calculation chain
CalculationChainPart calc = spreadsheet.WorkbookPart.CalculationChainPart;
spreadsheet.WorkbookPart.DeletePart(calc);
// Delete volatile dependencies
VolatileDependenciesPart vol = spreadsheet.WorkbookPart.VolatileDependenciesPart;
spreadsheet.WorkbookPart.DeletePart(vol);
}
}
// Remove printer settings
public void Remove_PrinterSettings(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
List<WorksheetPart> wsParts = spreadsheet.WorkbookPart.WorksheetParts.ToList();
foreach (WorksheetPart wsPart in wsParts)
{
List<SpreadsheetPrinterSettingsPart> printerList = wsPart.SpreadsheetPrinterSettingsParts.ToList();
foreach (SpreadsheetPrinterSettingsPart printer in printerList)
{
wsPart.DeletePart(printer);
Console.WriteLine("Printer setting was removed");
}
}
}
}
// Remove external cell references
public void Remove_CellReferences(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
List<WorksheetPart> worksheetparts = spreadsheet.WorkbookPart.WorksheetParts.ToList();
foreach (WorksheetPart part in worksheetparts)
{
Worksheet worksheet = part.Worksheet;
var rows = worksheet.GetFirstChild<SheetData>().Elements<Row>(); // Find all rows
foreach (var row in rows)
{
var cells = row.Elements<Cell>();
foreach (Cell cell in cells)
{
if (cell.CellFormula != null)
{
string formula = cell.CellFormula.InnerText;
if (formula.Length > 1)
{
string hit = formula.Substring(0, 1); // Transfer first 1 characters to string
string hit2 = formula.Substring(0, 2); // Transfer first 2 characters to string
if (hit == "[" || hit2 == "'[")
{
CellValue cellvalue = cell.CellValue; // Save current cell value
cell.CellFormula = null;
// If cellvalue does not have a real value
if (cellvalue.Text == "#N/A")
{
cell.DataType = CellValues.String;
cell.CellValue = new CellValue("Invalid data removed");
}
else
{
cell.CellValue = cellvalue; // Insert saved cell value
}
Console.WriteLine("External cell reference was removed");
}
}
}
}
}
}
// Delete external book references
List<ExternalWorkbookPart> extwbParts = spreadsheet.WorkbookPart.ExternalWorkbookParts.ToList();
if (extwbParts.Count > 0)
{
foreach (ExternalWorkbookPart extpart in extwbParts)
{
var elements = extpart.ExternalLink.ChildElements.ToList();
foreach (var element in elements)
{
if (element.LocalName == "externalBook")
{
spreadsheet.WorkbookPart.DeletePart(extpart);
}
}
}
}
// Delete calculation chain
CalculationChainPart calc = spreadsheet.WorkbookPart.CalculationChainPart;
spreadsheet.WorkbookPart.DeletePart(calc);
// Delete defined names that includes external cell references
DefinedNames definedNames = spreadsheet.WorkbookPart.Workbook.DefinedNames;
if (definedNames != null)
{
var definedNamesList = definedNames.ToList();
foreach (DefinedName definedName in definedNamesList)
{
if (definedName.InnerXml.StartsWith("["))
{
definedName.Remove();
}
}
}
}
}
// Remove external object references
public void Remove_ExternalObjects(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
IEnumerable<ExternalWorkbookPart> extWbParts = spreadsheet.WorkbookPart.ExternalWorkbookParts;
foreach (ExternalWorkbookPart extWbPart in extWbParts)
{
List<ExternalRelationship> extrels = extWbPart.ExternalRelationships.ToList(); // Must be a list
foreach (ExternalRelationship extrel in extrels)
{
Uri uri = new Uri($"External reference {extrel.Uri} was removed", UriKind.Relative);
extWbPart.DeleteExternalRelationship(extrel.Id);
extWbPart.AddExternalRelationship(relationshipType: "http://purl.oclc.org/ooxml/officeDocument/relationships/oleObject", externalUri: uri, id: extrel.Id);
}
}
}
}
// Make first sheet active sheet
public void Activate_FirstSheet(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
BookViews bookViews = spreadsheet.WorkbookPart.Workbook.GetFirstChild<BookViews>();
WorkbookView workbookView = bookViews.GetFirstChild<WorkbookView>();
if (workbookView.ActiveTab != null)
{
var activeSheetId = workbookView.ActiveTab.Value;
if (activeSheetId > 0)
{
workbookView.ActiveTab.Value = 0;
List<WorksheetPart> worksheets = spreadsheet.WorkbookPart.WorksheetParts.ToList();
foreach (WorksheetPart worksheet in worksheets)
{
var sheetviews = worksheet.Worksheet.SheetViews.ToList();
foreach (SheetView sheetview in sheetviews)
{
sheetview.TabSelected = null;
Console.WriteLine("First sheet was activated");
}
}
}
}
}
}
// Remove absolute path to local directory
public void Remove_AbsolutePath(string filepath)
{
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true, new OpenSettings()
{
MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(MarkupCompatibilityProcessMode.ProcessAllParts, FileFormatVersions.Office2013)
}))
{
if (spreadsheet.WorkbookPart.Workbook.AbsolutePath != null)
{
AbsolutePath absPath = spreadsheet.WorkbookPart.Workbook.AbsolutePath;
absPath.Remove();
Console.WriteLine("Absolute path to local directory removed");
}
}
}
public void Convert_EmbeddedImages(string filepath)
{
List<ImagePart> emf = new List<ImagePart>();
List<ImagePart> images = new List<ImagePart>();
// Open spreadsheet
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(filepath, true))
{
IEnumerable<WorksheetPart> worksheetParts = spreadsheet.WorkbookPart.WorksheetParts;
foreach (WorksheetPart worksheetPart in worksheetParts)
{
// Perform check
emf = worksheetPart.ImageParts.Distinct().ToList();
if (worksheetPart.DrawingsPart != null) // DrawingsPart needs a null check
{
images = worksheetPart.DrawingsPart.ImageParts.Distinct().ToList();
}
// Perform change
// Convert Excel-generated .emf images to TIFF
foreach (ImagePart imagePart in emf)
{
Convert_EmbedEmf(filepath, worksheetPart, imagePart);
}
// Convert embedded images to TIFF
foreach (ImagePart imagePart in images)
{
Convert_EmbedImg(filepath, worksheetPart, imagePart);
}
}
}
}
// Convert embedded images to TIFF
public void Convert_EmbedImg(string filepath, WorksheetPart worksheetPart, ImagePart imagePart)
{
// Convert streamed image to new stream
Stream stream = imagePart.GetStream();
Stream new_Stream = Convert_ImageMagick(stream);
stream.Dispose();
// Add new ImagePart
ImagePart new_ImagePart = worksheetPart.DrawingsPart.AddImagePart(ImagePartType.Tiff);
// Save image from stream to new ImagePart
new_Stream.Position = 0;
new_ImagePart.FeedData(new_Stream);
// Change relationships of image
string id = Get_RelationshipId(imagePart);
Blip blip = worksheetPart.DrawingsPart.WorksheetDrawing.Descendants<DocumentFormat.OpenXml.Drawing.Spreadsheet.Picture>()
.Where(p => p.BlipFill.Blip.Embed == id)
.Select(p => p.BlipFill.Blip)
.Single();
blip.Embed = Get_RelationshipId(new_ImagePart);
// Delete original ImagePart
worksheetPart.DrawingsPart.DeletePart(imagePart);
}
// Convert Excel-generated .emf images to TIFF
public void Convert_EmbedEmf(string filepath, WorksheetPart worksheetPart, ImagePart imagePart)
{
// Convert streamed image to new stream
Stream stream = imagePart.GetStream();
Stream new_Stream = Convert_ImageMagick(stream);
stream.Dispose();
// Add new ImagePart
ImagePart new_ImagePart = worksheetPart.VmlDrawingParts.First().AddImagePart(ImagePartType.Tiff);
// Save image from stream to new ImagePart
new_Stream.Position = 0;
new_ImagePart.FeedData(new_Stream);
// Change relationships of image
string id = Get_RelationshipId(imagePart);
XDocument xElement = worksheetPart.VmlDrawingParts.First().GetXDocument();
IEnumerable<XElement> descendants = xElement.FirstNode.Document.Descendants();
foreach (XElement descendant in descendants)
{
if (descendant.Name == "{urn:schemas-microsoft-com:vml}imagedata")
{
IEnumerable<XAttribute> attributes = descendant.Attributes();
foreach (XAttribute attribute in attributes)
{
if (attribute.Name == "{urn:schemas-microsoft-com:office:office}relid")
{
if (attribute.Value == id)
{
attribute.Value = Get_RelationshipId(new_ImagePart);
worksheetPart.VmlDrawingParts.First().SaveXDocument();
}
}
}
}
}
// Delete original ImagePart
worksheetPart.VmlDrawingParts.First().DeletePart(imagePart);
}
// Convert embedded object to TIFF using ImageMagick
public Stream Convert_ImageMagick(Stream stream)
{
// Read the input stream in ImageMagick
using (MagickImage image = new MagickImage(stream))
{
// Set input stream position to beginning
stream.Position = 0;
// Create a memorystream to write image to
MemoryStream new_stream = new MemoryStream();
// Adjust TIFF settings
image.Format = MagickFormat.Tiff;
image.Settings.ColorSpace = ColorSpace.RGB;
image.Settings.Depth = 32;
image.Settings.Compression = CompressionMethod.LZW;
// Write image to stream
image.Write(new_stream);
// Return the memorystream
return new_stream;
}
}
// Get relationship id of an OpenXmlPart
public string Get_RelationshipId(OpenXmlPart part)
{
string id = "";
IEnumerable<OpenXmlPart> parentParts = part.GetParentParts();
foreach (OpenXmlPart parentPart in parentParts)
{
if (parentPart.ToString() == "DocumentFormat.OpenXml.Packaging.DrawingsPart")
{
id = parentPart.GetIdOfPart(part);
return id;
}
else if (parentPart.ToString() == "DocumentFormat.OpenXml.Packaging.VmlDrawingPart")
{
id = parentPart.GetIdOfPart(part);
return id;
}
else if (parentPart.ToString() == "DocumentFormat.OpenXml.Packaging.Model3DReferenceRelationshipPart")
{
id = parentPart.GetIdOfPart(part);
return id;
}
else if (parentPart.ToString() == "DocumentFormat.OpenXml.Packaging.EmbeddedPackagePart")
{
id = parentPart.GetIdOfPart(part);
return id;
}
else if (parentPart.ToString() == "DocumentFormat.OpenXml.Packaging.OleObjectPart")
{
id = parentPart.GetIdOfPart(part);
return id;
}
}
return id;
}
// If file is ODS, use external app
public void ODS(string filepath, bool strict)
{
Process app = new Process();
app.StartInfo.UseShellExecute = false;
app.StartInfo.FileName = "javaw";
// If app is run on Windows
string? dir = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
dir = Environment.GetEnvironmentVariable("ODS-ArchivalRequirements");
}
if (dir != null)
{
app.StartInfo.FileName = dir;
}
else
{
app.StartInfo.FileName = "C:\\Program Files\\ODS-ArchivalRequirements\\ODS-ArchivalRequirements.jar";
}
if (strict)
{
app.StartInfo.Arguments = $"--inputfilepath \"{filepath}\" --change --policy-strict";
}
else
{
app.StartInfo.Arguments = $"--inputfilepath \"{filepath}\" --change";
}
app.Start();
app.WaitForExit();
app.Close();
}
}
}