Import macro to excel using open xml c# - c#

I am able to copy a vbaproject part from xlsm to another xlsm through memory stream and add it through addnew part. Is there any possibility to import/add only the bas file into the xlsm file using open xml.
Can some one assist.Below code helped to take a clone of vba part and update the same.
private static void cloneVbaPart(string src, string dst)
{
using (WordprocessingDocument srcDoc = WordprocessingDocument.Open(src, false))
{
var vbaPart = srcDoc.MainDocumentPart.VbaProjectPart;
using (WordprocessingDocument dstDoc = WordprocessingDocument.Open(dst, true))
{
var partsToRemove = new List<OpenXmlPart>();
foreach (var part in dstDoc.MainDocumentPart.GetPartsOfType<VbaProjectPart>())
{
partsToRemove.Add(part);
}
foreach (var part in dstDoc.MainDocumentPart.GetPartsOfType<CustomizationPart>())
{
partsToRemove.Add(part);
}
foreach (var part in partsToRemove)
{ dstDoc.MainDocumentPart.DeletePart(part); }
var vbaProjectPart = dstDoc.MainDocumentPart.AddNewPart<VbaProjectPart>();
var vbaDataPart = vbaProjectPart.AddNewPart<VbaDataPart>();
using (Stream data = vbaPart.GetStream())
{
vbaProjectPart.FeedData(data);
}
using (Stream data = vbaPart.VbaDataPart.GetStream())
{
vbaDataPart.FeedData(data);
}
}
}
}

Related

NAudio code holds lock on input file

My code below holds a lock on the input file preventing me from working with it later:
public static void ToWave_WMF(string source, string destination)
{
using (var reader = new MediaFoundationReader(source))
using (var rateSampler = new MediaFoundationResampler(reader, new WaveFormat(DefaultEncoding.SampleRate, reader.WaveFormat.Channels)))
using (var channelSampler = new MediaFoundationResampler(rateSampler, new WaveFormat(rateSampler.WaveFormat.SampleRate, DefaultEncoding.Channels)))
{
WaveFileWriter.CreateWaveFile(destination, channelSampler);
}
}
public static string BuildWavFile(string userFileLocation)
{
var sampleList = new List<ISampleProvider>();
try
{
// Add input file
var waveFile = AudioHelpers.ToWave_WMF(userFileLocation);
sampleList.Add(new AudioFileReader(waveFile));
WaveFileWriter.CreateWaveFile16(sirenWaveFile, new ConcatenatingSampleProvider(sampleList));
}
finally
{
foreach (var sample in sampleList)
{
((AudioFileReader)sample).Dispose();
}
}
return sirenWaveFile;
}
Am I using resources wrong? Why is this lock being held? If I delete the file after toWave_WMF() there is no issue. If I use sampleList.Add(new AudioFileReader(userFileLocation)); I also dont have any issues deleting the userFile.

“corrupt” document when Streaming In Memory Merge Word Document using OpenXML

I am trying merge several Word Document using OpenXML on ASP.NET MVC 5. But I am constantly getting a message from Microsoft Word that the document is corrupt.
private Stream GenerateDocument(DocumentType documentType)
{
using (var templateStream = File.OpenRead(GetTemplatePath(documentType)))
{
//some code
var result = documentGenerator.Generate();
return result;
}
}
private Stream MergeDocuments(DocumentLibraryModel documentLibrary)
{
var documentTypes = documentLibrary.DocumentTypes.GetEnumerator();
var mainStream = GenerateDocument(documentTypes.Current);
using (WordprocessingDocument mainDocument = WordprocessingDocument.Open(mainStream, true))
{
XElement newBody = XElement.Parse(mainDocument.MainDocumentPart.Document.Body.OuterXml);
documentTypes.MoveNext();
while (documentTypes.MoveNext())
{
WordprocessingDocument tempDocument = WordprocessingDocument.Open(GenerateDocument(documentTypes.Current), true);
XElement tempBody = XElement.Parse(tempDocument.MainDocumentPart.Document.Body.OuterXml);
newBody.Add(tempBody);
mainDocument.MainDocumentPart.Document.Body = new Body(newBody.ToString());
mainDocument.MainDocumentPart.Document.Save();
mainDocument.Package.Flush();
}
}
return mainStream;
}
However the document opens as corrupted.
Any ideas?
Problem lies in this:
XElement tempBody = XElement.Parse(tempDocument.MainDocumentPart.Document.Body.OuterXml);
newBody.Add(tempBody);
You are adding body to body which generates invalid Word document. Word document can contain only one Body at the time.
I would recommend cloning elements instead of parsing XML.
You can do this:
using (WordprocessingDocument mainDocument = WordprocessingDocument.Open(mainStream, true))
{
mainDocument.MainDocumentPart.Document.Body = new Body();
documentTypes.MoveNext();
while (documentTypes.MoveNext())
{
using (WordprocessingDocument tempDocument = WordprocessingDocument.Open(GenerateDocument(documentTypes.Current)))
{
foreach (var element in tempDocument.MainDocumentPart.Document.Body.Elements)
{
mainDocument.MainDocumentPart.Document.Body.AppendChild(element.CloneNode(true));
}
}
}
mainDocument.MainDocumentPart.Document.Save();
}

import named destinations in pdf

I m developing an application in which a word document is converted in pdf. My problem is too complicated please help me out.
My word doc has a toc, bookmarks, endnotes and hyperlinks. when I save this doc as pdf, only bookmarks are converted. After a long research I found that PDF documents does not support bookmark to bookmark hyperlinks, it needs either page number or named destinations.
So I choose named destinations for this purpose, but I am stuck again , because simple "save as" cannot generate named destinations in the pdf doc. So I print the word doc on adobe PDF printer and I got named destination as required, but again this document neither have bookmarks in it nor hyperlinks. so what I decided that I generate two pdf from a word, first by save as option and second one is by printing.
test.pdf (by save as) (contains bookmarks, hyperlinks)
test_p.pdf( by printing) (only contains named destination)
then I research ones again and found a way to extract all named destination from test_p.pdf into XML by a function of itextsharp.but unfortunately I dont get any way to import back this xml in test.pdf.. thats why I came here.
Guide me what to do next if this approach is ok. else suggest me any ohter approach to accomplish this mission.
I wrote a class to replace urls in my PDF files some times ago:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iTextSharp.text.pdf;
namespace ReplaceLinks
{
public class ReplacePdfLinks
{
Dictionary<string, PdfObject> _namedDestinations;
PdfReader _reader;
public string InputPdf { set; get; }
public string OutputPdf { set; get; }
public Func<Uri, string> UriToNamedDestination { set; get; }
public void Start()
{
updatePdfLinks();
saveChanges();
}
private PdfArray getAnnotationsOfCurrentPage(int pageNumber)
{
var pageDictionary = _reader.GetPageN(pageNumber);
var annotations = pageDictionary.GetAsArray(PdfName.ANNOTS);
return annotations;
}
private static bool hasAction(PdfDictionary annotationDictionary)
{
return annotationDictionary.Get(PdfName.SUBTYPE).Equals(PdfName.LINK);
}
private static bool isUriAction(PdfDictionary annotationAction)
{
return annotationAction.Get(PdfName.S).Equals(PdfName.URI);
}
private void replaceUriWithLocalDestination(PdfDictionary annotationAction)
{
var uri = annotationAction.Get(PdfName.URI) as PdfString;
if (uri == null)
return;
if (string.IsNullOrWhiteSpace(uri.ToString()))
return;
var namedDestination = UriToNamedDestination(new Uri(uri.ToString()));
if (string.IsNullOrWhiteSpace(namedDestination))
return;
PdfObject entry;
if (!_namedDestinations.TryGetValue(namedDestination, out entry))
return;
annotationAction.Remove(PdfName.S);
annotationAction.Remove(PdfName.URI);
var newLocalDestination = new PdfArray();
annotationAction.Put(PdfName.S, PdfName.GOTO);
var xRef = ((PdfArray)entry).First(x => x is PdfIndirectReference);
newLocalDestination.Add(xRef);
newLocalDestination.Add(PdfName.FITH);
annotationAction.Put(PdfName.D, newLocalDestination);
}
private void saveChanges()
{
using (var fileStream = new FileStream(OutputPdf, FileMode.Create, FileAccess.Write, FileShare.None))
using (var stamper = new PdfStamper(_reader, fileStream))
{
stamper.Close();
}
}
private void updatePdfLinks()
{
_reader = new PdfReader(InputPdf);
_namedDestinations = _reader.GetNamedDestinationFromStrings();
var pageCount = _reader.NumberOfPages;
for (var i = 1; i <= pageCount; i++)
{
var annotations = getAnnotationsOfCurrentPage(i);
if (annotations == null || !annotations.Any())
continue;
foreach (var annotation in annotations.ArrayList)
{
var annotationDictionary = (PdfDictionary)PdfReader.GetPdfObject(annotation);
if (!hasAction(annotationDictionary))
continue;
var annotationAction = annotationDictionary.Get(PdfName.A) as PdfDictionary;
if (annotationAction == null)
continue;
if (!isUriAction(annotationAction))
continue;
replaceUriWithLocalDestination(annotationAction);
}
}
}
}
}
To use it:
new ReplacePdfLinks
{
InputPdf = #"test.pdf",
OutputPdf = "mod.pdf",
UriToNamedDestination = uri =>
{
if (uri.Host.ToLowerInvariant().Contains("google.com"))
{
return "entry1";
}
return string.Empty;
}
}.Start();
This sample will modify all of the urls containing google.com to point to a specific named destination "entry1".
And this is the sample file to test the above class:
void WriteFile()
{
using (var doc = new Document(PageSize.LETTER))
{
using (var fs = new FileStream("test.pdf", FileMode.Create))
{
using (var writer = PdfWriter.GetInstance(doc, fs))
{
doc.Open();
var blueFont = FontFactory.GetFont("Arial", 12, Font.NORMAL, BaseColor.BLUE);
doc.Add(new Chunk("Go to URL", blueFont).SetAction(new PdfAction("http://www.google.com/", false)));
doc.NewPage();
doc.Add(new Chunk("Go to Test", blueFont).SetLocalGoto("entry1"));
doc.NewPage();
doc.Add(new Chunk("Test").SetLocalDestination("entry1"));
doc.Close();
}
}
}
}

Writing to file using values from an object in C#

My objective with this code is to use the foreach loop to go through each object and write the current string value to a txt file.
I'm using "Woof" and "Bull" as a test. Bull is the string variable in my AverageValues class.
Unfortunately, it currently will not write the value of bull to the file, however, it will create the file.
I think this is something easy to fix, I'm just can't seem to find it right now.
All help would be appreciated!
public void doStuff()
{
AverageValues AVS = new AverageValues();
AVS.Bull = "Woof";
string path = "C:\\users\\kjenks11\\Averages.txt";
FileStream NewFile = File.Create(path);
StreamWriter writeIt = new StreamWriter(NewFile);
List<AverageValues> AV = new List<AverageValues>();
AV.Add(AVS);
foreach (var value in AV)
{
writeIt.Write(value.Bull);
}
NewFile.Close();
}
You need to flush to write the data to the file. You also might consider adding using statements to your writer to free the resources when you're done.
public void doStuff()
{
AverageValues AVS = new AverageValues();
AVS.Bull = "Woof";
string path = "C:\\users\\kjenks11\\Averages.txt";
using (var NewFile = File.Create(path))
{
using (var writeIt = new StreamWriter(NewFile))
{
List<AverageValues> AV = new List<AverageValues> {AVS};
foreach (var value in AV)
{
writeIt.Write(value.Bull);
}
}
}
}
Flush or close the stream before closing the file itself:
foreach (var value in AV)
{
writeIt.WriteLine(value.Bull);
}
writeIt.Flush();
writeIt.Close();
Note on style - when creating a Stream (of any kind), or rather, any object that implements IDisposable, create it with a using statement:
using(var writeIt = new StreamWriter(NewFile))
{
// use writeIt here - it will dispose properly
}
If you use using around your stream, there's no need to call flush and close explicitly. As soon as you leave the using block' scope the stream will be closed and disposed. The close will call flush for you.
public void doStuff()
{
AverageValues AVS = new AverageValues();
AVS.Bull = "Woof";
string path = "C:\\users\\kjenks11\\Averages.txt";
FileStream NewFile = File.Create(path);
List<AverageValues> AV = new List<AverageValues>();
AV.Add(AVS);
using(StreamWriter writeIt = new StreamWriter(NewFile))
{
foreach (var value in AV)
{
writeIt.Write(value.Bull);
}
}
NewFile.Close();
}

How to delete a line having line number using c#?

My file named as test.txt contains
This document is divided into about 5 logical sections starting with a feature and structure overview, followed by an overview of built in column and cell types. Next is an overview of working with data, followed by an overview of specific major features. Lastly, a “best practice” section concludes the main part of this document.
Now i want to delete 2nd line of the file.
How to do it using c#?
Thanks in advance.
Naveenkumar
List<string> lines = File.ReadAllLines(#"filename.txt").ToList();
if(lines.Count>lineNum){
lines.RemoveAt(lineNum);
}
File.WriteAllLines(#"filename.txt",lines.ToArray());
You can acheive this by splitting the text by \n and then using LINQ to select the lines you want to keep, and re-joining them.
var lineNum=5;
var lines=File
.ReadAllText(#"src.txt")
.Split('\n');
var outTxt=String
.Join(
"\n",
lines
.Take(lineNum)
.Concat(lines.Skip(lineNum+1))
.ToArray()
);
Here's a pretty efficient way to do it.
FileInfo x = new FileInfo(#"path\to\original");
string xpath = x.FullName;
FileInfo y = new FileInfo(#"path\to\temporary\new\file");
using (var reader = x.OpenText())
using (var writer = y.AppendText())
{
// write 1st line
writer.WriteLine(reader.ReadLine());
reader.ReadLine(); // skip 2nd line
// write all remaining lines
while (!reader.EndOfStream)
{
writer.WriteLine(reader.ReadLine());
}
}
x.Delete();
y.MoveTo(xpath);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace rem2ndline
{
class Program
{
static void Main(string[] args)
{
string inPath = #"c:\rem2ndline.txt";
string outPath = #"c:\rem2ndlineresult.txt";
StringBuilder builder = new StringBuilder();
using (FileStream fso = new FileStream(inPath, FileMode.Open))
{
using (StreamReader rdr = new StreamReader(fso))
{
int lineCount = 0;
bool canRead = true;
while (canRead)
{
var line = rdr.ReadLine();
lineCount++;
if (line == null)
{
canRead = false;
}
else
{
if (lineCount != 2)
{
builder.AppendLine(line);
}
}
}
}
}
using(FileStream fso2 = new FileStream(outPath, FileMode.OpenOrCreate))
{
using (StreamWriter strw = new StreamWriter(fso2))
{
strw.Write(builder.ToString());
}
}
}
}
}
Here's what I'd do. The advantage is that you don't have to have the file in memory all at once, so memory requirements should be similar for files of varying sizes (as long as the lines contained in each of the files are of similar length). The drawback is that you can't pipe back to the same file - you have to mess around with a Delete and a Move afterwards.
The extension methods may be overkill for your simple example, but those are two extension methods I come to rely on again and again, as well as the ReadFile method, so I'd typically only have to write the code in Main().
class Program
{
static void Main()
{
var file = #"C:\myFile.txt";
var tempFile = Path.ChangeExtension(file, "tmp");
using (var writer = new StreamWriter(tempFile))
{
ReadFile(file)
.FilterI((i, line) => i != 1)
.ForEach(l => writer.WriteLine(l));
}
File.Delete(file);
File.Move(tempFile, file);
}
static IEnumerable<String> ReadFile(String file)
{
using (var reader = new StreamReader(file))
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
}
}
static class IEnumerableExtensions
{
public static IEnumerable<T> FilterI<T>(
this IEnumerable<T> seq,
Func<Int32, T, Boolean> filter)
{
var index = 0;
foreach (var item in seq)
{
if (filter(index, item))
{
yield return item;
}
index++;
}
}
public static void ForEach<T>(
this IEnumerable<T> seq,
Action<T> action)
{
foreach (var item in seq)
{
action(item);
}
}
}

Categories

Resources