Memory leak with a memory stream - c#

I know this code is far from perfect but in my case this was the only
way to do it correctly because im embedding WPF in C#, and when
applying text regulary the Spellcheck does not work correctly
So this is my code:
RichTextBox temphotfix = new RichTextBox();
temphotfix.Font = new Font(temphotfix.Font.Name, 14);
System.Windows.Documents.TextRange range = new System.Windows.Documents.TextRange(omschrijving.Document.ContentStart, omschrijving.Document.ContentEnd);
temphotfix.Text = oms;
string temp = temphotfix.Rtf;
byte[] byteArray = Encoding.ASCII.GetBytes(temp);
MemoryStream stream = new MemoryStream(byteArray);
range.Load(stream, DataFormats.Rtf);
range = null;
temp = null;
byteArray = null;
temphotfix.Dispose();
stream.Dispose();
I stress tested this, and it seems like ever about 5 times the script gets ran, it adds about 1 MB ram.
What am i doing wrong, i litterly made everyting i used null, or desposed them.

As I told above in comment you can using, you can try this code. hope this should help.
using (RichTextBox temphotfix = new RichTextBox())
{
temphotfix.Font = new Font(temphotfix.Font.Name, 14);
System.Windows.Documents.TextRange range = new System.Windows.Documents.TextRange(omschrijving.Document.ContentStart, omschrijving.Document.ContentEnd);
temphotfix.Text = oms;
string temp = temphotfix.Rtf;
byte[] byteArray = Encoding.ASCII.GetBytes(temp);
using (MemoryStream stream = new MemoryStream(byteArray))
{
range.Load(stream, DataFormats.Rtf);
}
range = null;
temp = null;
byteArray = null;
//temphotfix.Dispose();
//stream.Dispose();
}

Related

Blazor WASM Load and display large pdfs by splitting them as streams

I'm working on a Blazor WASM App and I want my users to easily open pdf files on specific pages that contain additional information.
I cannot distribute those files myself or upload them to any kind of server. Each user has to provide them themselves.
Because the files are up to 60MB big I cannot convert the uploaded file to base64 and display them as described here.
However I don't have to display the whole file and could just load the needed page +- some pages around them.
For that I tried using iText7 ExtractPageRange(). This answer indicates, that I have to override the GetNextPdfWriter() Method and to store all streams in an collection.
class ByteArrayPdfSplitter : PdfSplitter {
public ByteArrayPdfSplitter(PdfDocument pdfDocument) : base(pdfDocument) {
}
protected override PdfWriter GetNextPdfWriter(PageRange documentPageRange) {
CurrentMemoryStream = new MemoryStream();
UsedStreams.Add(CurrentMemoryStream);
return new PdfWriter(CurrentMemoryStream);
}
public MemoryStream CurrentMemoryStream { get; private set; }
public List<MemoryStream> UsedStreams { get; set; } = new List<MemoryStream>();
Then I thought I could merge those streams and convert them to base64
var file = loadedFiles.First();
using (MemoryStream ms = new MemoryStream())
{
var rs = file.OpenReadStream(maxFileSize);
await rs.CopyToAsync(ms);
ms.Position = 0;
//rs needed to be converted to ms, because the PdfReader constructer uses a
//synchronious read that isn't supported by rs and throws an exception.
PdfReader pdfReader = new PdfReader(ms);
var document = new PdfDocument(pdfReader);
var splitter = new ByteArrayPdfSplitter(document);
var range = new PageRange();
range.AddPageSequence(1, 10);
var splitDoc = splitter.ExtractPageRange(range);
//Edit commented this out, shouldn't have been here at all leads to an exception
//splitDoc.Close();
var outputMs = new MemoryStream();
foreach (var usedMs in splitter.UsedStreams)
{
usedMs.Position = 0;
outputMs.Position = outputMs.Length;
await usedMs.CopyToAsync(outputMs);
}
var data = outputMs.ToArray();
currentPdfContent = "data:application/pdf;base64,";
currentPdfContent += Convert.ToBase64String(data);
pdfLoaded = true;
}
This however doesn't work.
Has anyone a suggestion how to get this working? Or maybe a simpler solution I could try.
Edit:
I took a closer look in debug and it seems like, the resulting stream outputMs is always empty. So it is probably a problem in how I split the pdf.
After at least partially clearing up my misconception of what it means to not being able to access the file system from blazor WASM I managed to find a working solution.
await using MemoryStream ms = new MemoryStream();
var rs = file.OpenReadStream(maxFileSize);
await using var fs = new FileStream("test.pdf", FileMode.Create)
fs.Position = 0;
await rs.CopyToAsync(fs);
fs.Close();
string path = "test.pdf";
string range = "10 - 15";
var pdfDocument = new PdfDocument(new PdfReader("test.pdf"));
var split = new MySplitter(pdfDocument);
var result = split.ExtractPageRange(new PageRange(range));
result.Close();
await using var splitFs = new FileStream("split.pdf", FileMode.Open))
await splitFs.CopyToAsync(ms);
var data = ms.ToArray();
var pdfContent = "data:application/pdf;base64,";
pdfContent += System.Convert.ToBase64String(data);
Console.WriteLine(pdfContent);
currentPdfContent = pdfContent;
With the MySplitter Class from this answer.
class MySplitter : PdfSplitter
{
public MySplitter(PdfDocument pdfDocument) : base(pdfDocument)
{
}
protected override PdfWriter GetNextPdfWriter(PageRange documentPageRange)
{
String toFile = "split.pdf";
return new PdfWriter(toFile);
}
}

How to output Metafile (emf) into stream (or byte[])

For an application I'm currently developing, I need to create a .emf file.
I've got it working when I output the result directly to a file, but I can't get it to output into a stream, which is essential to what I'm trying to do.
This code correctly generates the file, but it's outputted directly to the harddisk.
var sizedImage = new Bitmap(103, 67);
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile("result.emf", graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
Here's my attempt to output it to a memorystream, so I could get a byte[] from that stream:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
resultingBytes = stream.GetBuffer();
}
File.WriteAllBytes("result.emf", resultingBytes);
But all this does is create an empty file. When I run through it with the debugger, I can see the stream remain empty.
What am I missing here..?
I found the answer thanks to #Selvin
It turns out, the changes are only written to the MemoryStream when the "graphics" object is disposed. So, just by adding an extra set of brances, the problem is resolved.
Here's my working code:
byte[] resultingBytes;
var sizedImage = new Bitmap(103, 67);
using(var stream = new MemoryStream())
using(var graphicsFromSizedImage = Graphics.FromImage(sizedImage))
using(var metafile = new Metafile(stream, graphicsFromSizedImage.GetHdc()))
{
using(var graphics = Graphics.FromImage(metafile))
{
graphics.DrawStuff()
graphicsFromSizedImage.ReleaseHdc();
}
resultingBytes = stream.ToArray();
}
File.WriteAllBytes("result.emf", resultingBytes);
Edit: As some have pointed out, stream.GetBuffer() will return the entire buffer. I changed it to stream.ToArray(), which should be better.

Set Bitmap as cover art for MP3

I have been trying to set a bitmap as cover art for a MP3 but I can't seem to get it working. It isn't throwing any errors but when I play the MP3 the bitmap isn't showing.
This is what I currently have:
TagLib.File f = TagLib.File.Create("song.mp3");
Image currentImage = getAlbumArt(result.passedAlbumID);
Picture pic = new Picture();
pic.Type = PictureType.FrontCover;
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
pic.Description = "Cover";
MemoryStream ms = new MemoryStream();
currentImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Position = 0;
pic.Data = ByteVector.FromStream(ms);
f.Tag.Pictures = new IPicture[1] { pic };
pictureBox1.Image = currentImage; //testing the image is correct
f.Save();
ms.Close();
I'm using the following code and everything works fine for me:
TagLib.File file = TagLib.File.Create(/*path to your mp3 file*/);
TagLib.Picture pic = new TagLib.Picture();
pic.Type = TagLib.PictureType.FrontCover;
pic.Description = "Cover";
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
MemoryStream ms = new MemoryStream();
/*your image*/.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Position = 0;
pic.Data = TagLib.ByteVector.FromStream(ms);
file.Tag.Pictures = new TagLib.IPicture[] { pic };
file.Save();
ms.Close();
According to your provided code, the only thing I noticed is, that my code is using following line
file.Tag.Pictures = new TagLib.IPicture[] { pic };
instead of
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
So simply try, if it works when you remove the 1 inside the square brackets.

C# ZipOutputStream getting invalid files from output stream

I've been playing around with C#'s SharpZip Library (version 0.86.0). I'm basically using it to package a number of files into one clean zip files. Here's what my function looks like to do generate the byte array of the zip file:
public static byte[] CompressToZip(List<Tuple<byte[], string>> fileItemList, int zipLevel = 3)
{
MemoryStream zipMemoryStream = new MemoryStream();
ZipOutputStream zOutput = new ZipOutputStream(zipMemoryStream);
zOutput.SetLevel(zipLevel);
ICSharpCode.SharpZipLib.Checksums.Crc32 crc = new ICSharpCode.SharpZipLib.Checksums.Crc32();
foreach (var file in fileItemList)
{
ZipEntry entry = new ZipEntry(file.Item2);
entry.DateTime = DateTime.Now;
entry.Size = file.Item1.Length;
crc.Reset();
crc.Update(file.Item1);
entry.Crc = crc.Value;
zOutput.PutNextEntry(entry);
zOutput.Write(file.Item1, 0, file.Item1.Length);
}
zOutput.IsStreamOwner = false;
zOutput.Finish();
zOutput.Close();
zipMemoryStream.Position = 0;
byte[] zipedFile = zipMemoryStream.ToArray();
return zipedFile;
}
The function works fine for files with one item in it. But for some reason when I have two or more I get errors when I go to extract/open it.
PeaZip says:
Archive is not readable
WinZip says:
The compressed size stored in the local header for this file is not the same as the compressed size stored in the central header
but here's the kicker. Windows 8 Archiving tool works just fine with the file. The WinZip error kind of makes me think I'm writing the files to the stream incorrectly. But it looks fine to me. Not sure what to make of this..
EDIT
Here's my changes from codemonkeys input. Looks better to me, but I'm still getting the same errors
public static byte[] CompressToZip(List<Tuple<byte[], string>> fileItemList, int zipLevel = 3)
{
MemoryStream zipMemoryStream = new MemoryStream();
ZipOutputStream zOutput = new ZipOutputStream(zipMemoryStream);
zOutput.SetLevel(zipLevel);
ICSharpCode.SharpZipLib.Checksums.Crc32 crc = new ICSharpCode.SharpZipLib.Checksums.Crc32();
foreach (var file in fileItemList)
{
ZipEntry entry = new ZipEntry(file.Item2);
entry.DateTime = DateTime.Now;
entry.Size = file.Item1.Length;
crc.Reset();
crc.Update(file.Item1);
entry.Crc = crc.Value;
zOutput.PutNextEntry(entry);
var memStreamCurrentfile = new MemoryStream(file.Item1);
StreamUtils.Copy(memStreamCurrentfile, zOutput, new byte[4096]);
zOutput.CloseEntry();
}
zOutput.IsStreamOwner = false;
zOutput.Finish();
zOutput.Close();
zipMemoryStream.Position = 0;
byte[] zipedFile = zipMemoryStream.ToArray();
return zipedFile;
}
Figured it out! It seems me setting the Crc and size of file entry was the issue. I assumed it would helped to define those. I guess I was wrong. Here's the final code for all to enjoy:
public static byte[] CompressToZip(List<Tuple<byte[], string>> fileItemList, int zipLevel = 3)
{
MemoryStream zipMemoryStream = new MemoryStream();
ZipOutputStream zOutput = new ZipOutputStream(zipMemoryStream);
zOutput.SetLevel(zipLevel);
ICSharpCode.SharpZipLib.Checksums.Crc32 crc = new ICSharpCode.SharpZipLib.Checksums.Crc32();
foreach (var file in fileItemList)
{
ZipEntry entry = new ZipEntry(file.Item2);
entry.DateTime = DateTime.Now;
zOutput.PutNextEntry(entry);
var memStreamCurrentfile = new MemoryStream(file.Item1);
StreamUtils.Copy(memStreamCurrentfile, zOutput, new byte[4096]);
zOutput.CloseEntry();
}
zOutput.IsStreamOwner = false;
zOutput.Finish();
zOutput.Close();
zipMemoryStream.Position = 0;
byte[] zipedFile = zipMemoryStream.ToArray();
return zipedFile;
}

How to add multiple fpages to fixed document in xps?

My requirement is to create xps document which has 10 pages (say). I am using the following code to create a xps document. Please take a look.
// Create the new document
XpsDocument xd = new XpsDocument("D:\\9780545325653.xps", FileAccess.ReadWrite);
IXpsFixedDocumentSequenceWriter xdSW = xd.AddFixedDocumentSequence();
IXpsFixedDocumentWriter xdW = xdSW.AddFixedDocument();
IXpsFixedPageWriter xpW = xdW.AddFixedPage();
fontURI = AddFontResourceToFixedPage(xpW, #"D:\arial.ttf");
image = AddJpegImageResourceToFixedPage(xpW, #"D:\Single content\20_1.jpg");
StringBuilder pageContents = new StringBuilder();
pageContents.Append(ReadFile(#"D:\Single content\20.fpage\20.fpage", i));
xmlWriter = xpW.XmlWriter;
xmlWriter.WriteRaw(pageContents.ToString());
}
xmlWriter.Close();
xpW.Commit();
// Commit the fixed document
xdW.Commit();
// Commite the fixed document sequence writer
xdSW.Commit();
// Commit the XPS document itself
xd.Close();
}
private static string AddFontResourceToFixedPage(IXpsFixedPageWriter pageWriter, String fontFileName)
{
string fontUri = "";
using (XpsFont font = pageWriter.AddFont(false))
{
using (Stream dstFontStream = font.GetStream())
using (Stream srcFontStream = File.OpenRead(fontFileName))
{
CopyStream(srcFontStream, dstFontStream);
// commit font resource to the package file
font.Commit();
}
fontUri = font.Uri.ToString();
}
return fontUri;
}
private static Int32 CopyStream(Stream srcStream, Stream dstStream)
{
const int size = 64 * 1024; // copy using 64K buffers
byte[] localBuffer = new byte[size];
int bytesRead;
Int32 bytesMoved = 0;
// reset stream pointers
srcStream.Seek(0, SeekOrigin.Begin);
dstStream.Seek(0, SeekOrigin.Begin);
// stream position is advanced automatically by stream object
while ((bytesRead = srcStream.Read(localBuffer, 0, size)) > 0)
{
dstStream.Write(localBuffer, 0, bytesRead);
bytesMoved += bytesRead;
}
return bytesMoved;
}
private static string ReadFile(string filePath,int i)
{
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);
StringBuilder sb = new StringBuilder();
using (StreamReader sr = new StreamReader(fs))
{
String line;
// Read and display lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
}
string allines = sb.ToString();
//allines = allines.Replace("FontUri=\"/Resources/f7728e4c-2606-4fcb-b963-d2d3f52b013b.odttf\"", "FontUri=\"" + fontURI + "\" ");
//XmlReader xmlReader = XmlReader.Create(fs, new XmlReaderSettings() { IgnoreComments = true });
XMLSerializer serializer = new XMLSerializer();
FixedPage fp = (FixedPage)serializer.DeSerialize(allines, typeof(FixedPage));
foreach (Glyphs glyph in fp.lstGlyphs)
{
glyph.FontUri = fontURI;
}
fp.Path.PathFill.ImageBrush.ImageSource = image;
fs.Close();
string fpageString = serializer.Serialize(fp);
return fpageString;
}
private static string AddJpegImageResourceToFixedPage(IXpsFixedPageWriter pageWriter, String imgFileName)
{
XpsImage image = pageWriter.AddImage("image/jpeg");
using (Stream dstImageStream = image.GetStream())
using (Stream srcImageStream = File.OpenRead(imgFileName))
{
CopyStream(srcImageStream, dstImageStream); // commit image resource to the package file
//image.Commit();
}
return image.Uri.ToString();
}
If you see it, i would have passed single image and single fpage to create a xps document. I want to pass multiple fpages list and image list to create a xps document which has multiple pages..?
You are doing this in the most excruciatingly difficult manner possible. I'd suggest taking the lazy man's route.
Realize that an XpsDocument is just a wrapper on a FixedDocumentSequence, which contains zero or more FixedDocuments, which contains zero or more FixedPages. All these types can be created, manipulated and combined without writing XML.
All you really need to do is create a FixedPage with whatever content on it you need. Here's an example:
static FixedPage CreateFixedPage(Uri imageSource)
{
FixedPage fp = new FixedPage();
fp.Width = 320;
fp.Height = 240;
Grid g = new Grid();
g.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
g.VerticalAlignment = System.Windows.VerticalAlignment.Center;
fp.Children.Add(g);
Image img = new Image
{
UriSource = imageSource,
};
g.Children.Add(image);
return fp;
}
This is all WPF. I'm creating a FixedPage that has as its root a Grid, which contains an Image that is loaded from the given Uri. The image will be stretched to fill the available space of the Grid. Or, you could do whatever you want. Create a template as a UserControl, send it text to place within itself, whatever.
Next, you just need to add a bunch of fixed pages to an XpsDocument. It's incredibly hard, so read carefully:
public void WriteAllPages(XpsDocument document, IEnumerable<FixedPage> pages)
{
var writer = XpsDocument.CreateXpsDocumentWriter(document);
foreach(var page in pages)
writer.Write(page);
}
And that's all you need to do. Create your pages, add them to your document. Done.

Categories