Powerpoint shows "Unable to read an object" after I embed the excel into powerpoint file via openxml.
Am using a simple excel with a chart and a pptx created already.
This fails in both .NetCore and .NetFramework
Here is my code.
string PPTFileName = #"c:\tmp\Example04.pptx";
string spreadsheetFileName = #"c:\tmp\demoOut5.xlsx";
using (PresentationDocument myPresDoc = PresentationDocument.Open(PPTFileName, true))
{
SlidePart slidePartBookMark = null;
string chartPartIdBookMark = "";
foreach (var slidePart in myPresDoc.PresentationPart.SlideParts)
{
if (slidePart.ChartParts.Any())
{
slidePartBookMark = slidePart;
var chartPart = slidePart.ChartParts.First();
chartPartIdBookMark = slidePart.GetIdOfPart(chartPart);
slidePart.DeletePart(chartPart);
slidePart.Slide.Save();
break;
}
}
var newChartPart = slidePartBookMark.AddNewPart<ChartPart>(chartPartIdBookMark);
EmbeddedPackagePart embPackage = newChartPart
.AddNewPart<EmbeddedPackagePart>("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "rId1");
embPackage.FeedData(new FileStream(#"c:\tmp\demoOut4.xlsx", FileMode.Open, FileAccess.Read));
DocumentFormat.OpenXml.Drawing.Charts.ExternalData ed = new DocumentFormat.OpenXml.Drawing.Charts.ExternalData();
ed.Id = "rId1";
slidePartBookMark.Slide.Save();
myPresDoc.Close();
Console.WriteLine("Done");
Console.ReadKey();
}
Any help or direction would be appreciated. Thanks.
Related
i am new C# Beginner,
I created a function to generate Excel using ClosedXML.Excel, but i also want to zip the excel fill, how to zip excel file using Sharpziplib? anybody could give an adivse?thanks
public FileResult Export()
{
DataTable test = new DataTable("test");
test.Columns.AddRange(new DataColumn[] {new DataColumn("Name")});
foreach (var record in recordtest)
{
test.Rows.Add(record.name);
}
using (XLWorkbook ts = new XLWorkbook())
{
var testSheet = ts.Worksheets.Add(test);
testSheet.Cell("D4").Value = "First Name";
testSheet.Range("A4:A5").Merge();
testSheet.Range("A4:Q5").Columns().Style.Fill.BackgroundColor =
XLColor.Almond;
}
using (MemoryStream steam = new MemoryStream())
{
wb.SaveAs(steam);
var fileName = String.Format("{0}-{1}.xlsx", "test",
DateTime.Now.ToString("yyyyMMdd"));
return File(steam.ToArray(), "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet", fileName);
}
}
If you want create zip, there are same way with CreateZipFile following this articles
https://github.com/icsharpcode/SharpZipLib/tree/master/samples/ICSharpCode.SharpZipLib.Samples/cs/CreateZipFile
https://ourcodeworld.com/articles/read/629/how-to-create-and-extract-zip-files-compress-and-decompress-zip-with-sharpziplib-with-csharp-in-winforms
I am trying to create a process in .NET to convert a PDF and all it's pages + attachments to PNGs. I am evaluating libraries and came across PDFiumSharp but it is not working for me. Here is my code:
string Inputfile = "input.pdf";
string OutputFolder = "Output";
string fileName = Path.GetFileNameWithoutExtension(Inputfile);
using (PdfDocument doc = new PdfDocument(Inputfile))
{
for (int i = 0; i < doc.Pages.Count; i++)
{
var page = doc.Pages[i];
using (var bitmap = new PDFiumBitmap((int)page.Width, (int)page.Height, false))
{
page.Render(bitmap);
var targetFile = Path.Combine(OutputFolder, fileName + "_" + i + ".png");
bitmap.Save(targetFile);
}
}
}
When I run this code, I get this exception:
screenshot of exception
Does anyone know how to fix this? Also does PDFiumSharp support extracting PDF attachments? If not, does anyone have any other ideas on how to achieve my goal?
PDFium does not look like it supports extracting PDF attachments. If you want to achieve your goal, then you can take a look at another library that supports both extracting PDF attachments as well as converting PDFs to PNGs.
I am an employee of the LEADTOOLS PDF SDK which you can try out via these 2 nuget packages:
https://www.nuget.org/packages/Leadtools.Pdf/
https://www.nuget.org/packages/Leadtools.Document.Sdk/
Here is some code that will convert a PDF + all attachments in the PDF to separate PNGs in an output directory:
SetLicense();
cache = new FileCache { CacheDirectory = "cache" };
List<LEADDocument> documents = new List<LEADDocument>();
if (!Directory.Exists(OutputDir))
Directory.CreateDirectory(OutputDir);
using var document = DocumentFactory.LoadFromFile("attachments.pdf", new LoadDocumentOptions { Cache = cache, LoadAttachmentsMode = DocumentLoadAttachmentsMode.AsAttachments });
if (document.Pages.Count > 0)
documents.Add(document);
foreach (var attachment in document.Attachments)
documents.Add(document.LoadDocumentAttachment(new LoadAttachmentOptions { AttachmentNumber = attachment.AttachmentNumber }));
ConvertDocuments(documents, RasterImageFormat.Png);
And the ConvertDocuments method:
static void ConvertDocuments(IEnumerable<LEADDocument> documents, RasterImageFormat imageFormat)
{
using var converter = new DocumentConverter();
using var ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
ocrEngine.Startup(null, null, null, null);
converter.SetOcrEngineInstance(ocrEngine, false);
converter.SetDocumentWriterInstance(new DocumentWriter());
foreach (var document in documents)
{
var name = string.IsNullOrEmpty(document.Name) ? "Attachment" : document.Name;
string outputFile = Path.Combine(OutputDir, $"{name}.{RasterCodecs.GetExtension(imageFormat)}");
int count = 1;
while (File.Exists(outputFile))
outputFile = Path.Combine(OutputDir, $"{name}({count++}).{RasterCodecs.GetExtension(imageFormat)}");
var jobData = new DocumentConverterJobData
{
Document = document,
Cache = cache,
DocumentFormat = DocumentFormat.User,
RasterImageFormat = imageFormat,
RasterImageBitsPerPixel = 0,
OutputDocumentFileName = outputFile,
};
var job = converter.Jobs.CreateJob(jobData);
converter.Jobs.RunJob(job);
}
}
Using .net core & c# here.
I have a UI from which user can upload the Excel or CSV files. Once they upload this goes to my web api which handles the reading of the data from these files and returns json.
My Api code as:
[HttpPost("upload")]
public async Task<IActionResult> FileUpload(IFormFile file)
{
JArray data = new JArray();
using (ExcelPackage package = new ExcelPackage(file.OpenReadStream()))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
//Process, read from excel here and populate jarray
}
return Ok(data );
}
In my above code I am using EPPlus for reading the excel file. For excel file it works all fine but it cannot read csv file which is the limitation of EPPlus.
I searched and found another library CSVHelper: https://joshclose.github.io/CsvHelper/ The issue with this is it does vice versa and can read from CSV but not from Excel.
Is there any library available which supports reading from both.
Or would it be possible use EPPlus only but convert uploaded CSV to excel on the fly and then read. (please note I am not storing the excel file anywhere so cant use save as to save it as excel)
Any inputs please?
--Updated - Added code for reading data from excel---
int rowCount = worksheet.Dimension.End.Row;
int colCount = worksheet.Dimension.End.Column;
for (int row = 1; row <= rowCount; row++)
{
for (int col = 1; col <= colCount; col++)
{
var rowValue = worksheet.Cells[row, col].Value;
}
}
//With the code suggested in the answer rowcount is always 1
You can use EPPLus and a MemoryStream for opening csv files into an ExcelPackage without writing to a file. Below is an example. You may have to change some of the the parameters based on your CSV file specs.
[HttpPost("upload")]
public async Task<IActionResult> FileUpload(IFormFile file)
{
var result = string.Empty;
string worksheetsName = "data";
bool firstRowIsHeader = false;
var format = new ExcelTextFormat();
format.Delimiter = ',';
format.TextQualifier = '"';
using (var reader = new System.IO.StreamReader(file.OpenReadStream()))
using (ExcelPackage package = new ExcelPackage())
{
result = reader.ReadToEnd();
ExcelWorksheet worksheet =
package.Workbook.Worksheets.Add(worksheetsName);
worksheet.Cells["A1"].LoadFromText(result, format, OfficeOpenXml.Table.TableStyles.Medium27, firstRowIsHeader);
}
}
Here's using Aspose, which is unfortunately not free, but wow it works great. My API is using the streaming capability with Content-Type: multipart/form-data rather than the IFormFile implementation:
[HttpPut]
[DisableFormValueModelBinding]
public async Task<IActionResult> UploadSpreadsheet()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
return BadRequest($"Expected a multipart request, but got {Request.ContentType}");
}
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = (await reader.ReadNextSectionAsync()).AsFileSection();
//If you're doing CSV, you add this line:
LoadOptions loadOptions = new LoadOptions(LoadFormat.CSV);
var workbook = new Workbook(section.FileStream, loadOptions);
Cells cells = workbook.Worksheets[0].Cells;
var rows = cells.Rows.Cast<Row>().Where(x => !x.IsBlank);
//Do whatever else you want here
Please try with below code
private string uploadCSV(FileUpload fl)
{
string fileName = "";
serverLocation = Request.PhysicalApplicationPath + "ExcelFiles\\";
fileName = fl.PostedFile.FileName;
int FileSize = fl.PostedFile.ContentLength;
string contentType = fl.PostedFile.ContentType;
fl.PostedFile.SaveAs(serverLocation + fileName);
string rpath = string.Empty, dir = string.Empty;
HttpContext context = HttpContext.Current;
string baseUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.ApplicationPath.TrimEnd('/') + '/';
try
{
rpath = serverLocation + fileName;//Server.MapPath(dir + fileName);
using (Stream InputStream = fl.PostedFile.InputStream)
{
Object o = new object();
lock (o)
{
byte[] buffer = new byte[InputStream.Length];
InputStream.Read(buffer, 0, (int)InputStream.Length);
lock (o)
{
File.WriteAllBytes(rpath, buffer);
buffer = null;
}
InputStream.Close();
}
}
}
catch (Exception ex)
{
lblSOTargetVal.Text = ex.Message.ToString();
}
return rpath;
}
Use the Open XML SDK package and add insert working solution for it.
In short: I would like to insert the content of a docx that contains images and bullets in another docx.
My problem: I used two approaches:
Manual merge
Altchunk
With both of them I got a corrupted word document as result.
If I remove the images from the docx that I would like to insert in another one, the result docx is OK.
My code:
Manual merge (thanks to https://stackoverflow.com/a/48870385/10075827):
private static void ManualMerge(string firstPath, string secondPath, string resultPath)
{
if (!System.IO.Path.GetFileName(firstPath).StartsWith("~$"))
{
File.Copy(firstPath, resultPath, true);
using (WordprocessingDocument result = WordprocessingDocument.Open(resultPath, true))
{
using (WordprocessingDocument secondDoc = WordprocessingDocument.Open(secondPath, false))
{
OpenXmlElement p = result.MainDocumentPart.Document.Body.Descendants<Paragraph>().Last();
foreach (var e in secondDoc.MainDocumentPart.Document.Body.Elements())
{
var clonedElement = e.CloneNode(true);
clonedElement.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList().ForEach(blip =>
{
var newRelation = result.CopyImage(blip.Embed, secondDoc);
blip.Embed = newRelation;
});
clonedElement.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().ToList().ForEach(imageData =>
{
var newRelation = result.CopyImage(imageData.RelationshipId, secondDoc);
imageData.RelationshipId = newRelation;
});
result.MainDocumentPart.Document.Body.Descendants<Paragraph>().Last();
if (clonedElement is Paragraph)
{
p.InsertAfterSelf(clonedElement);
p = clonedElement;
}
}
}
}
}
}
public static string CopyImage(this WordprocessingDocument newDoc, string relId, WordprocessingDocument org)
{
var p = org.MainDocumentPart.GetPartById(relId) as ImagePart;
var newPart = newDoc.MainDocumentPart.AddPart(p);
newPart.FeedData(p.GetStream());
return newDoc.MainDocumentPart.GetIdOfPart(newPart);
}
Altchunk merge (from http://www.karthikscorner.com/sharepoint/use-altchunk-document-assembly/):
private static void AltchunkMerge(string firstPath, string secondPath, string resultPath)
{
WordprocessingDocument mainDocument = null;
MainDocumentPart mainPart = null;
var ms = new MemoryStream();
#region Prepare - consuming application
byte[] bytes = File.ReadAllBytes(firstPath);
ms.Write(bytes, 0, bytes.Length);
mainDocument = WordprocessingDocument.Open(ms, true);
mainPart = mainDocument.MainDocumentPart;
#endregion
#region Document to be imported
FileStream fileStream = new FileStream(secondPath, FileMode.Open);
#endregion
#region Merge
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, "AltChunkId101");
chunk.FeedData(fileStream);
var altChunk = new AltChunk(new AltChunkProperties() { MatchSource = new MatchSource() { Val = new OnOffValue(true) } });
altChunk.Id = "AltChunkId101";
mainPart.Document.Body.InsertAfter(altChunk, mainPart.Document.Body.Elements<Paragraph>().Last());
mainPart.Document.Save();
#endregion
#region Mark dirty
var listOfFieldChar = mainPart.Document.Body.Descendants<FieldChar>();
foreach (FieldChar current in listOfFieldChar)
{
if (string.Compare(current.FieldCharType, "begin", true) == 0)
{
current.Dirty = new OnOffValue(true);
}
}
#endregion
#region Save Merged Document
mainPart.DocumentSettingsPart.Settings.PrependChild(new UpdateFieldsOnOpen() { Val = new OnOffValue(true) });
mainDocument.Close();
FileStream file = new FileStream(resultPath, FileMode.Create, FileAccess.Write);
ms.WriteTo(file);
file.Close();
ms.Close();
#endregion
}
I spent hours searching for a solution and the most common one I found was to use altchunk. So why is it not working in my case?
If you are able to use the Microsoft.Office.Interop.Word namespace, and able to put a bookmark in the file you want to merge into, you can take this approach:
using Microsoft.Office.Interop.Word;
...
// merge by putting second file into bookmark in first file
private static void NewMerge(string firstPath, string secondPath, string resultPath, string firstBookmark)
{
var app = new Application();
var firstDoc = app.Documents.Open(firstPath);
var bookmarkRange = firstDoc.Bookmarks[firstBookmark];
// Collapse the range to the end, as to not overwrite it. Unsure if you need this
bookmarkRange.Collapse(WdCollapseDirection.wdCollapseEnd);
// Insert into the selected range
// use if relative path
bookmarkRange.InsertFile(Environment.CurrentDirectory + secondPath);
// use if absolute path
//bookmarkRange.InsertFile(secondPath);
}
Related:
C#: Insert and indent bullet points at bookmark in word document using Office Interop libraries
I am making a docx generator using Docx.dll. So far i have been able to insert images and text into the document. The images and paragraph are not aligned. I need to wrap text the image. How do i do it?
I looked for it in google and found this link
Adding Images to Documents in Word 2007 by Using the Open XML SDK 2.0. The code is working and creating the word document too, but the docx file is not opening.
How do i wrap text 'In Front Of Text' in c#?
public static DocX CreateDocumentFile(List<CompanyInfo> info)
{
DocX document = DocX.Load(#"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\RetailWrite.docx");
foreach (var companies in info)
{
Formatting fm = new Formatting();
/*Inserting Image*/
Novacode.Image img = document.AddImage(#"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\logos\slime.png");
Novacode.Paragraph companyLogo = document.InsertParagraph("");
Picture pic1 = img.CreatePicture();
companyLogo.InsertPicture(pic1, 0);
Novacode.Paragraph CompanyName = document.InsertParagraph(companies.Name.ToString());
CompanyName.StyleName = "COMPANY";
Novacode.Paragraph CompanyPosition = document.InsertParagraph(companies.Position.ToString());
CompanyPosition.StyleName = "posit";
Novacode.Paragraph CompanyDescription = document.InsertParagraph(companies.Description.ToString());
CompanyDescription.StyleName = "descrip";
Novacode.Paragraph blankPara = document.InsertParagraph(" ");
Novacode.Paragraph blankPara2 = document.InsertParagraph(" ");
}
return document;
}
Solution to the problem: I used the Interop of MS-Word to apply word-wrap across images.
public static void FormatImages()
{
Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application();
string filePath = #"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\Temp.docx";
Microsoft.Office.Interop.Word.Document doc = wordApp.Documents.Open(filePath, false);
object save_changes = false;
foreach (Microsoft.Office.Interop.Word.InlineShape item in wordApp.ActiveDocument.InlineShapes)
{
if (item != null)
{
if (item.Type == Microsoft.Office.Interop.Word.WdInlineShapeType.wdInlineShapePicture)
{
item.Select();
Microsoft.Office.Interop.Word.Shape shape = item.ConvertToShape();
shape.WrapFormat.Type = WdWrapType.wdWrapFront;
}
}
}
doc.SaveAs(#"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\RetailWrite.docx");
doc.Close(save_changes);
wordApp.Quit(save_changes);
if (System.IO.File.Exists(#"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\Temp.docx"))
{
System.IO.File.Delete(#"C:\Users\newton.sheikh\Documents\Visual Studio 2010\Projects\MSOffice\OpenXML\OpenXML\Temp.docx");
}
}