I am trying top open a PDF file using iTextSharp, add a dataset to the file to prepopulate data, then save it to a stream so that I can display it to the users. I don't want to save it locally to a file. I keep getting the error "Cannot access a closed Stream." I can't figure out which stream is wrong.
Here is my code:
public FileStreamResult PushDataIntoPDFStream(string filename)
{
var reader = new PdfReader(Path.Combine(Server.MapPath(path), filename));
var xml = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<form1>
<firstName>test</firstName>
<lastName>user</lastName>
<driveCar>0</driveCar>
<gender>1</gender>
<birthdate>2011-08-12</birthdate>
<numPets>4</numPets>
</form1>";
using (var outstream = new MemoryStream())
{
using (var stamper = new PdfStamper(reader, outstream))
{
var bytes = System.Text.Encoding.UTF8.GetBytes(xml);
using (var ms = new MemoryStream(bytes))
{
stamper.AcroFields.Xfa.FillXfaForm(ms);
}
}
return new FileStreamResult(outstream, "application/pdf")
{
FileDownloadName = "file.pdf";
};
}
}
The PdfStamper has a .CloseStream property on its internal output stream; try setting it to false:
using (var stamper = new PdfStamper(reader, outstream))
{
stamper.Writer.CloseStream = false;
Related
I am trying to convert a FlowDocument to XPS. Here is how the FlowDocument is defined:
<FlowDocument PageHeight="29.7cm" PageWidth="21cm" PagePadding="2cm,2cm,2cm,2cm">
</FlowDocument>
It is inside a RichTextBox and is populated by a user. It is saved as a .rtf file (.xamlgave me the same results).
Here is the method I am using to save the doc:
public void UploadTemplate(TextRange content, string filename)
{
string destPath = Path.Combine(default_template_path, filename + ".rtf");
if (content.CanSave(DataFormats.Rtf))
{
using (var stream = new FileStream(destPath, FileMode.Create))
{
content.Save(stream, DataFormats.Rtf);
stream.Close();
}
}
}
And here is how I load the doc:
public void LoadTemplate(string template_path, TextRange content)
{
if (content.CanLoad(DataFormats.Rtf))
{
using (var stream = new FileStream(template_path, FileMode.Open))
{
content.Load(stream, DataFormats.Rtf);
stream.Close();
}
}
}
Finally this is the code I use for converting the FlowDocument to XPS:
public static MemoryStream FlowDocumentToXPS(FlowDocument flowDocument)
{
MemoryStream stream = new MemoryStream();
using (Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite))
{
using (XpsDocument xpsDoc = new XpsDocument(package, CompressionOption.Maximum))
{
XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
paginator.PageSize = new Size(flowDocument.PageWidth, flowDocument.PageHeight);
rsm.SaveAsXaml(paginator);
rsm.Commit();
}
}
stream.Position = 0;
Console.WriteLine(stream.Length);
Console.WriteLine(stream.Position);
return stream;
}
After saving this stream to a .xps file the document pages seem like they are divided in 2 columns. This is what I get from that export. Can someone help me figure this out ?
For anyone that might stumble upon this. You must set the FlowDocument.ColumnWidth property when converting to XPS. In this case it would need to be something like this:
public static MemoryStream FlowDocumentToXPS(FlowDocument flowDocument)
{
flowDocument.ColumnWidth = (int)YourColumnWidth; // THIS IS THE KEY LINE
MemoryStream stream = new MemoryStream();
using (Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite))
{
using (XpsDocument xpsDoc = new XpsDocument(package, CompressionOption.Maximum))
{
XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
paginator.PageSize = new Size(flowDocument.PageWidth, flowDocument.PageHeight);
rsm.SaveAsXaml(paginator);
rsm.Commit();
}
}
stream.Position = 0;
Console.WriteLine(stream.Length);
Console.WriteLine(stream.Position);
return stream;
}
I have successfully done "find and replace" which created an xml. Now I want to convert the newly created xml file to pdf which will be attached as a file and sent in a mail.
The result of the Base64String was tested on a base64 pdf file converter but the pdf cannot be opened. Got this error: Something went wrong couldn't open the file
HOW CAN I MAKE THIS WORK?
public async Task<string> CreateDocument(string PolicyNumber)
{
var policy = await _context.Policy.SingleOrDefaultAsync(p => p.PolicyNumber == PolicyNumber);
ArgumentNullException.ThrowIfNull(policy, "Policy Not Available");
//CreatePolicyDocument
//create policy document
var files = #"C:\Users\PATHTODOCUMENT\holderTest.docx";
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(files, true))
{
string docText;
using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}
Regex regexText = new Regex("XCONCLUSION_DATEX");
var newWordText = regexText.Replace(docText, "Hi Everyone!");
using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))
{
sw.Write(newWordText);
Encoding encoding = Encoding.UTF8;
byte[] docAsBytes = encoding.GetBytes(newWordText);
File.WriteAllBytes("hello.pdf", docAsBytes);
var file = Convert.ToBase64String(docAsBytes);
}
}
//send message
//
return "";
}
PDF file has its own construct,so you can't generate a pdf file with the contentstring of xml file dirctly.
If you really want to convert xml contentstring to PDF,you could try iTextSharp.
I tried with a simple demo,and here's the code:
[HttpPost]
public IActionResult XMLtoPDF([FromForm] Try T1)
{
var streamContent = new StreamContent(T1.file.OpenReadStream());
var filestring = streamContent.ReadAsStringAsync().Result;
MemoryStream outputStream = new MemoryStream();
Document doc = new Document();
PdfWriter writer = PdfWriter.GetInstance(doc, outputStream);
//PDF settings
PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f);
doc.Open();
var paragraph = new Paragraph(filestring);
doc.Add(paragraph);
doc.Close();
outputStream.Close();
var pdfArray = outputStream.ToArray();
return File(pdfArray, "application/pdf");
}
Result:
I need to create a zip file in memory, then send the zip file to the client. However, there are cases where the created zip file will need to contain other zip files that were also generated in memory. For instance, the file structure might look like this:
SendToClient.zip
InnerZip1.zip
File1.xml
File2.xml
InnerZip2.zip
File3.xml
File4.xml
I've been attempting to use the System.IO.Compression.ZipArchive library. I cannot use the System.IO.Compression.ZipFile library because my project's version of .NET is not compatible with it.
Here's an example of what I've tried.
public Stream GetMemoryStream() {
var memoryStream = new MemoryStream();
string fileContents = "Lorem ipsum dolor sit amet";
string entryName = "Lorem.txt";
string innerZipName = "InnerZip.zip";
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
ZipArchiveEntry entry = archive.CreateEntry(Path.Combine(innerZipName, entryName), CompressionLevel.Optimal);
using (var writer = new StreamWriter(entry.Open())) {
writer.Write(fileContents);
}
}
return memoryStream
}
However, this just puts Lorem.txt in a folder called "Inner.zip" (instead of in an actual zip file).
I can create an empty inner zip file if I create an entry called "Inner.zip" without writing to it. I can't add anything to it, though, and writing to an entry called "Inner.zip\Lorem.txt" afterward just creates a folder again (alongside the identically named empty .zip file).
I've also tried creating a separate archive, serializing it with a memory stream, then writing that to the original archive as a .zip.
public Stream CreateOuterZip() {
var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
ZipArchiveEntry entry = archive.CreateEntry("Outer.zip", CompressionLevel.NoCompression);
using (var writer = new BinaryWriter(entry.Open())) {
writer.Write(GetMemoryStream().ToArray());
}
}
return memoryStream;
}
This just creates an invalid .zip file that windows doesn't know how to open, though.
Thanks in advance!
So I created a FileStream instead of a MemoryStream so the code can be tested easier
public static Stream CreateOuterZip()
{
string fileContents = "Lorem ipsum dolor sit amet";
// Final zip file
var fs = new FileStream(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SendToClient.zip"), FileMode.OpenOrCreate);
// Create inner zip 1
var innerZip1 = new MemoryStream();
using (var archive = new ZipArchive(innerZip1, ZipArchiveMode.Create, true))
{
var file1 = archive.CreateEntry("File1.xml");
using (var writer = new BinaryWriter(file1.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
var file2 = archive.CreateEntry("File2.xml");
using (var writer = new BinaryWriter(file2.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
}
// Create inner zip 2
var innerZip2 = new MemoryStream();
using (var archive = new ZipArchive(innerZip2, ZipArchiveMode.Create, true))
{
var file3 = archive.CreateEntry("File3.xml");
using (var writer = new BinaryWriter(file3.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
var file4 = archive.CreateEntry("File4.xml");
using (var writer = new BinaryWriter(file4.Open()))
{
writer.Write(fileContents); // Change fileContents to real XML content
}
}
using (var archive = new ZipArchive(fs, ZipArchiveMode.Create, true))
{
// Create inner zip 1
var innerZipEntry = archive.CreateEntry("InnerZip1.zip");
innerZip1.Position = 0;
using (var s = innerZipEntry.Open())
{
innerZip1.WriteTo(s);
}
// Create inner zip 2
var innerZipEntry2 = archive.CreateEntry("InnerZip2.zip");
innerZip2.Position = 0;
using (var s = innerZipEntry2.Open())
{
innerZip2.WriteTo(s);
}
}
fs.Close();
return fs; // The file is written, can probably just close this
}
You can obviously modify this method to return a MemoryStream, or change the method to Void to just have the zip file written out to disk
You should create ZipArchive for internal zip file also. Write it to stream (memorystream). And after write this stream as general stream into main zip.
static Stream Inner() {
var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
var demoFile = archive.CreateEntry("foo2.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream)) {
streamWriter.Write("Bar2!");
}
}
return memoryStream;
}
static void Main(string[] args) {
using (var memoryStream = new MemoryStream()) {
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
var demoFile = archive.CreateEntry("foo.txt");
using (var entryStream = demoFile.Open())
using (var streamWriter = new StreamWriter(entryStream)) {
streamWriter.Write("Bar!");
}
var zip = archive.CreateEntry("inner.zip");
using (var entryStream = zip.Open()) {
var inner = Inner();
inner.Seek(0, SeekOrigin.Begin);
inner.CopyTo(entryStream);
}
}
using (var fileStream = new FileStream(#"d:\test.zip", FileMode.Create)) {
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
Thanks to this answer.
I working with PDF annotations using ITextSharp. I was able to add annotations pretty smoothly.
But now I'm trying to edit them. It looks like my PdfReader object is actually updated. But for some reason I can't save it. As shown in the snippet below, I try to get the byte array from using a stamper. The byte array is only 1 byte longer than the previous version no matter how long is the annotation. And when I open the PDF saved on the file system, I still have the old annotation...
private void UpdatePDFAnnotation(string title, string body)
{
byte[] newBuffer;
using (PdfReader pdfReader = new PdfReader(dataBuffer))
{
int pageIndex = 1;
int annotIndex = 0;
PdfDictionary pageDict = pdfReader.GetPageN(pageIndex);
var annots = pageDict.GetAsArray(PdfName.ANNOTS);
if (annots != null)
{
PdfDictionary annot = annots.GetAsDict(annotIndex);
annot.Put(PdfName.T, new PdfString(title));
annot.Put(PdfName.CONTENTS, new PdfString(body));
}
// ********************************
// this line shows the new annotation is in here. Just have to save it somehow !!
var updatedBody = pdfReader.GetPageN(pageIndex).GetAsArray(PdfName.ANNOTS).GetAsDict(0).GetAsString(PdfName.CONTENTS);
Debug.Assert(newBody == updatedBody.ToString(), "Annotation body should be equal");
using (MemoryStream outStream = new MemoryStream())
{
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
newBuffer = outStream.ToArray();
}
}
}
File.WriteAllBytes( #"Assets\Documents\AnnotedPdf.pdf", newBuffer);
}
Any idea what's wrong with my code?
PdfStamper does much of the writing at the time it is being closed. This implicitly happens at the end of its using block. But you retrieve the MemoryStream contents already in that block. Thus, the PDF is not yet written to the retrieved byte[].
Instead either explicitly close the PdfStamper instance before retrieving the byte[]:
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
stamp.Close();
newBuffer = outStream.ToArray();
}
or retrieve the byte[] after that using block:
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
}
newBuffer = outStream.ToArray();
Allright, I finally got it to work. The trick was the two last parameter in the PdfStamper instantiation. I tried it before with only 2 parameters and ended up with a corrupted file. Then I tried again and now it works... here's the snippet
private void UpdatePDFAnnotation(string title, string body)
{
using (PdfReader pdfReader = new PdfReader(dataBuffer))
{
PdfDictionary pageDict = pdfReader.GetPageN(pageIndex);
var annots = pageDict.GetAsArray(PdfName.ANNOTS);
PdfDictionary annot = annots.GetAsDict(annotIndex);
annot.Put(PdfName.T, new PdfString(title));
annot.Put(PdfName.CONTENTS, new PdfString(body));
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamp = new PdfStamper(pdfReader, ms);
stamp.Dispose();
dataBuffer = ms.ToArray();
}
}
}
I am using SSH.NET library to download files. I want to save the downloaded file as a file in memory, rather than a file on disk but it is not happening.
This is my code which works fine:
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
sftp.DownloadFile("AFile.txt", System.IO.File.Create("AFile.txt"));
sftp.Disconnect();
}
and this is the code which doesn't work fine as it gives 0 bytes stream.
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
sftp.DownloadFile("file.txt", mem);
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
string s = textReader.ReadToEnd(); // it is empty
sftp.Disconnect();
}
You can try the following code, which opens the file on the server and reads it back into a stream:
using (var sftp = new SftpClient(sFTPServer, sFTPUsername, sFTPPassword))
{
sftp.Connect();
// Load remote file into a stream
using (var remoteFileStream = sftp.OpenRead("file.txt"))
{
var textReader = new System.IO.StreamReader(remoteFileStream);
string s = textReader.ReadToEnd();
}
}
For simple text files, it's even easier:
var contents = sftp.ReadAllText(fileSpec);
I had a similar issue with the ScpClient, I needed to reset the stream position to the beginning after downloading the file.
using (var sftp = new SftpClient(sFTPServer, sFTPPassword, sFTPPassword))
{
sftp.Connect();
System.IO.MemoryStream mem = new System.IO.MemoryStream();
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
sftp.DownloadFile("file.txt", mem);
// Reset stream to the beginning
mem.Seek(0, SeekOrigin.Begin);
System.IO.TextReader textReader = new System.IO.StreamReader(mem);
string s = textReader.ReadToEnd();
sftp.Disconnect();
}