How to UnProtect Sheet using OpenXML code? - openxml

I have created one Sheet, which i could protect using OpenXml code.
But now there is requirement to read this excel file.
I am getting all the values as NULL because it is protected.
( I haven't placed any password yet in the code to protect the sheet, there is only one sheet in the excel file.)
I have got below code from my Search to unprotect the worksheet.
workSheet.RemoveAllChildren<SheetProtection>();
But, this is not working. I am still getting the null values while reading this protected sheet.
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(FilePath, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
//if ((sheets.Count() != 2) && (sheets.First().Name.Value != "StudentNomination") && (sheets.Last().Name.Value != "Sheet2"))
//{
// throw new Exception("Please Upload the correct Nomination file, for example you can download the Nomination Template file first.!!");
//}
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
workSheet.RemoveAllChildren<SheetProtection>();
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
Can anyone Please help me on this?

Your code for removing protection is correct. But before that you have to open the Excel file in Edit Mode. The second argument in SpreadSheetDocument.Open should be set to true.
Also Regardless of protection, you should be able to read a cell's value. See the below code. In order to test this you would have to create a excel file and fill the cells A1,B1 and C1 with numbers.
using System.Linq;
using System.Collections.Generic;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
class Test
{
static void Main()
{
string filePath = #"E:\test.xlsx";
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(filePath, true))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
var dataBeforeProtection = workSheet.Descendants<Row>().First().Descendants<Cell>().First().CellValue.InnerText;
workSheet.RemoveAllChildren<SheetProtection>();
var dataAfterProtection = workSheet.Descendants<Row>().First().Descendants<Cell>().First().CellValue.InnerText;
workSheet.Save();
}
}
}

Related

How can we write data to excel Template and maintain the template format in OpenXML?

I am able to read the excel template using openxml, and able to append data to the sheets.
But unable to maintain the existing Excel Template format, and unable to identify the column headings
Please find the below code used
if (ds.Tables.Count > 0 && ds.Tables[0] != null || ds.Tables[0].Columns.Count > 0)
{
System.Data.DataTable table = ds.Tables[0];
using (var spreadsheetDocument = SpreadsheetDocument.Open(filePath, true))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
Sheet sheet = spreadsheetDocument.WorkbookPart.Workbook.Sheets.GetFirstChild<Sheet>();
Worksheet worksheet = (spreadsheetDocument.WorkbookPart.GetPartById(sheet.Id.Value) as WorksheetPart).Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
List<String> columns = new List<string>();
IEnumerable<Row> rows = worksheet.GetFirstChild<SheetData>().Descendants<Row>();
foreach (DataColumn column in table.Columns)
{
columns.Add(column.ColumnName);
Cell cell = new Cell();
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(column.ColumnName);
}
foreach (DataRow row in table.Rows)
{
Row newRow = new Row();
columns.ForEach(col =>
{
Cell cell = new Cell();
//If value is DBNull, do not set value to cell
if (row[col] != System.DBNull.Value)
{
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(row[col].ToString());
}
newRow.AppendChild(cell);
});
sheetData.AppendChild(newRow);
}
spreadsheetDocument.WorkbookPart.Workbook.Save();
spreadsheetDocument.Close();
}
Please find actual excel screenshot
please find expected excel screenshot
Please clarify your specific problem and provided the actual code snippet and the example template that you are using so we can better understand your issue and assist you on the issue.
There is earlier post “OpenXML SDK 2.5 Excel Template" Find a label in template with name DataField, and replace with data. This might be what you are looking for.

How to remove the extra page at the end of a word document which created during mail merge

I have written a piece of code to create a word document by mail merge using Syncfusion (Assembly Syncfusion.DocIO.Portable, Version=17.1200.0.50,), Angular 7+ and .NET Core. Please see the code below.
private MemoryStream MergePaymentPlanInstalmentsScheduleToPdf(List<PaymentPlanInstalmentReportModel>
PaymentPlanDetails, byte[] templateFileBytes)
{
if (templateFileBytes == null || templateFileBytes.Length == 0)
{
return null;
}
var templateStream = new MemoryStream(templateFileBytes);
var pdfStream = new MemoryStream();
WordDocument mergeDocument = null;
using (mergeDocument = new WordDocument(templateStream, FormatType.Docx))
{
if (mergeDocument != null)
{
var mergeList = new List<PaymentPlanInstalmentScheduleMailMergeModel>();
var obj = new PaymentPlanInstalmentScheduleMailMergeModel();
obj.Applicants = 0;
if (PaymentPlanDetails != null && PaymentPlanDetails.Any()) {
var applicantCount = PaymentPlanDetails.GroupBy(a => a.StudentID)
.Select(s => new
{
StudentID = s.Key,
Count = s.Select(a => a.StudentID).Distinct().Count()
});
obj.Applicants = applicantCount?.Count() > 0 ? applicantCount.Count() : 0;
}
mergeList.Add(obj);
var reportDataSource = new MailMergeDataTable("Report", mergeList);
var tableDataSource = new MailMergeDataTable("PaymentPlanDetails", PaymentPlanDetails);
List<DictionaryEntry> commands = new List<DictionaryEntry>();
commands.Add(new DictionaryEntry("Report", ""));
commands.Add(new DictionaryEntry("PaymentPlanDetails", ""));
MailMergeDataSet ds = new MailMergeDataSet();
ds.Add(reportDataSource);
ds.Add(tableDataSource);
mergeDocument.MailMerge.ExecuteNestedGroup(ds, commands);
mergeDocument.UpdateDocumentFields();
using (var converter = new DocIORenderer())
{
using (var pdfDocument = converter.ConvertToPDF(mergeDocument))
{
pdfDocument.Save(pdfStream);
pdfDocument.Close();
}
}
mergeDocument.Close();
}
}
return pdfStream;
}
Once the document is generated, I notice there is a blank page (with the footer) at the end. I searched for a solution on the internet over and over again, but I was not able to find a solution. According to experts, I have done the initial checks such as making sure that the initial word template file has no page breaks, etc.
I am wondering if there is something that I can do from my code to remove any extra page breaks or anything like that, which can cause this.
Any other suggested solution for this, even including MS Word document modifications also appreciated.
Please refer the below documentation link to remove empty page at the end of Word document using Syncfusion Word library (Essential DocIO).
https://www.syncfusion.com/kb/10724/how-to-remove-empty-page-at-end-of-word-document
Please reuse the code snippet before converting Word to PDF in your sample application.
Note: I work for Syncfusion.

iText not returning text contents of a PDF after first page

I am trying to use the iText library with c# to capture the text portion of pdf files.
I created a pdf from excel 2013 (exported) and then copied the sample from the web of how to use itext (added the lib ref to the project).
It reads perfectly the first page but it gets garbled info after that. It is keeping part of the first page and merging the info with the next page. The commented lines is when I was trying to solve the problem, the string "thePage" is recreated inside the for loop.
Here is the code. I can email the pdf to whoever can help with this issue.
Thanks in advance
public static string ExtractTextFromPdf(string path)
{
ITextExtractionStrategy its = new iTextSharp.text.pdf.parser.LocationTextExtractionStrategy();
using (PdfReader reader = new PdfReader(path))
{
StringBuilder text = new StringBuilder();
//string[] theLines;
//theLines = new string[COLUMNS];
//string thePage;
for (int i = 1; i <= reader.NumberOfPages; i++)
{
string thePage = "";
thePage = PdfTextExtractor.GetTextFromPage(reader, i, its);
string [] theLines = thePage.Split('\n');
foreach (var theLine in theLines)
{
text.AppendLine(theLine);
}
// text.AppendLine(" ");
// Array.Clear(theLines, 0, theLines.Length);
// thePage = "";
}
return text.ToString();
}
}
A strategy object collects text data and does not know if a new page has started or not.
Thus, use a new strategy object for each page.

Openxml: Added ImagePart is not showing in Powerpoint / Missing RelationshipID

I'm trying to dynamically create a PowerPoint presentation. One slide has a bunch of placeholder images that need to be changed based on certain values.
My approach is to create a new ImagePart and link it to the according Blip. The image is downloaded and stored to the presentation just fine. The problem is, that there is no relationship created in slide.xml.rels file for the image, which leads to an warning about missing images and empty boxes on the slide.
Any ideas what I am doing wrong?
Thanks in advance for your help! Best wishes
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite siteCollection = new SPSite(SPContext.Current.Site.RootWeb.Url))
{
using (SPWeb oWeb = siteCollection.OpenWeb())
{
SPList pictureLibrary = oWeb.Lists[pictureLibraryName];
SPFile imgFile = pictureLibrary.RootFolder.Files[imgPath];
byte[] byteArray = imgFile.OpenBinary();
int pos = Convert.ToInt32(name.Replace("QQ", "").Replace("Image", ""));
foreach (DocumentFormat.OpenXml.Presentation.Picture pic in pictureList)
{
var oldimg = pic.BlipFill.Blip.Embed.ToString(); ImagePart ip = (ImagePart)slidePart.AddImagePart(ImagePartType.Png, oldimg+pos);
using (var writer = new BinaryWriter(ip.GetStream()))
{
writer.Write(byteArray);
}
string newId = slidePart.GetIdOfPart(ip);
setDebugMessage("new img id: " + newId);
pic.BlipFill.Blip.Embed = newId;
}
slidePart.Slide.Save();
}
}
});
So, for everyone who's experiencing a similar problem, I finally found the solution. Quite a stupid mistake. Instad of PresentationDocument document = PresentationDocument.Open(mstream, true); you have to use
using (PresentationDocument document = PresentationDocument.Open(mstream, true))
{
do your editing here
}
This answer brought me on the right way.

Syncfusion DocIO -- how to insert image (local file) at bookmark using BookmarksNavigator

I have been using Syncfusion DocIO for generating MS Word documents from my .net applications (winforms). So far I have dealt with plain text and it is fairly straightforward to insert text in a word document template where bookmarks serve as reference points for text insertion.
I am navigating the bookmarks using BookmarksNavigator.MoveToBookmark() . Now I need to insert an image at a bookmark but I am at a loss at how to go about it.
Please help...
Thanks.
Specifically for adding it to a bookmark :
//Move to the specified bookmark
bk.MoveToBookmark(bookmark);
//Insert the picture into the specified bookmark location
bk.DeleteBookmarkContent(true);
// we assume the text is a full pathname for an image file
// get the image file
System.Drawing.Image image = System.Drawing.Image.FromFile(sText);
IWParagraph paragraph = new WParagraph(document);
paragraph.AppendPicture(image);
bk.InsertParagraph(paragraph);
private System.Drawing.Image LoadSignature(string sFileName)
{
string sImagePath = sFileName;
System.Drawing.Image image = System.Drawing.Image.FromFile(sImagePath);
return image;
}
private void MergeSignature(WordDocument doc, string sFile, string sBalise)
{
System.Drawing.Image iSignature = LoadSignature(sFile);
WordDocument ImgDoc = new WordDocument();
ImgDoc.AddSection();
ImgDoc.Sections[0].AddParagraph().AppendPicture(iSignature);
if (iSignature != null)
{
TextSelection ts = null ;
Regex pattern = new Regex(sBalise);
ts = doc.Find(pattern);
if (ts != null)
{
doc.ReplaceFirst = true;
doc.Replace(pattern, ImgDoc, false);
}
}
iSignature.Dispose();
}
See here: https://help.syncfusion.com/file-formats/docio/working-with-mailmerge
1) You should create docx file with name "Template.docx". This file will use as template.
In your docx file create Field of type MergeField.
2) Create MergeFiled with name Image:Krishna
3)
using Syncfusion.DocIO.DLS;
using System.Drawing;
public class Source
{
public Image Krishna { get; set; } = Image.FromFile(#"C:\1.png");
}
and generating code:
public static void Generate()
{
WordDocument doc = new WordDocument("Template.docx");
Source data = new Source();
var dataTable = new MailMergeDataTable("", new Source[] { data });
doc.MailMerge.ExecuteGroup(dataTable);
doc.Save("result.docx");
doc.Close();
}