I am trying to read a template file from disk and add a paragraph to it. Once done, i want to open this as a new file . The template should not have the changes saved into it. I tried the code below and the result is , the template is getting modified with the change but the file that is opening in browser does not have those changes. Clearly I am not getting the modified stream in response. How do I do that and also avoid making change to the template file .
public class DocumentCreator
{
public static void CreateDoc()
{
string strDoc = #"C:\Ash\foo.docx";
string txt = "Bruno Rovani";
using (Stream stream = File.Open(strDoc, FileMode.Open))
{
OpenAndAddToWordprocessingStream(stream, txt);
}
}
public static void OpenAndAddToWordprocessingStream(Stream stream, string txt)
{
// Open a WordProcessingDocument based on a stream.
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(stream, true))
{
// Assign a reference to the existing document body.
Body body = wordprocessingDocument.MainDocumentPart.Document.Body;
// Add new text.
Paragraph para = body.AppendChild(new Paragraph());
Run run = para.AppendChild(new Run());
run.AppendChild(new Text(txt));
// HTTP response
HttpResponse Response = HttpContext.Current.Response;
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AddHeader("Content-Disposition", "attachment; filename=myfile.docx");
//stream = wordprocessingDocument.MainDocumentPart.GetStream();
stream.Position = 0;
stream.CopyTo(Response.OutputStream);
Response.Flush();
Response.End();
}
}
}
Resolved
Added wordprocessingDocument.MainDocumentPart.Document.Save();
PS: The template is still being modified. Don't think there is way to avoid this but since I will be doing a find and replace in real scenario it is not a big problem for me.
so the function looks like
public static void OpenAndAddToWordprocessingStream(Stream stream, string txt)
{
// Open a WordProcessingDocument based on a stream.
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(stream, true))
{
// Assign a reference to the existing document body.
Body body = wordprocessingDocument.MainDocumentPart.Document.Body;
// Add new text.
Paragraph para = body.AppendChild(new Paragraph());
Run run = para.AppendChild(new Run());
run.AppendChild(new Text(txt));
**wordprocessingDocument.MainDocumentPart.Document.Save();**
// HTTP response
HttpResponse Response = HttpContext.Current.Response;
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AddHeader("Content-Disposition", "attachment; filename=myfile.docx");
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(Response.OutputStream);
Response.Flush();
Response.End();
}
}
This scenario works fine without any images on the spreadsheet, but after attempting to add an image to the spreadsheets that get put in the zip file, the spreadsheets open with the excel error of "We found a problem with some content ....".
I have other methods using Epplus without DotNetZip that use the exact same code to insert the image into a spreadsheet and they work fine with no errors or issues.
Code that works to return a single spreadsheet with an image
public async Task<ActionResult> GenerateSpreadsheet(ReportViewModel reportViewModel)
{
using (var excelPackage = new ExcelPackage())
{
Bitmap logoFile = getLogoFile();
var companyLogo = worksheet.Drawings.AddPicture("File Name", logoFile);
companyLogo.From.Column = columnIndex - 4;
companyLogo.From.Row = rowIndex;
companyLogo.SetSize(logoFile.Width, logoFile.Height);
//Write all the stuff to the spreadsheet
Response.ClearContent();
Response.BinaryWrite(excelPackage.GetAsByteArray());
string fileName = "attachment;filename=Project_Report_Export.xlsx";
Response.AddHeader("content-disposition", fileName);
Response.ContentType = "application/excel";
Response.Flush();
Response.End();
}
}
Code that will build a spreadsheet, add it to a zip file, but where the spreadsheet will open with the "We found a problem with some content ...." if an image was added to the spreadsheet as shown below. If there is no image added to it, it will open without the error.
public async Task<ActionResult> GenerateSpreadsheet(ReportViewModel reportViewModel)
{
using (var stream = new MemoryStream())
{
using (ZipFile zip = new ZipFile())
{
foreach(var spreadSheet in listOfStuffToBuildFrom)
{
using (var excelPackage = new ExcelPackage())
{
Bitmap logoFile = getLogoFile();
var companyLogo = worksheet.Drawings.AddPicture("File Name", logoFile);
companyLogo.From.Column = columnIndex - 4;
companyLogo.From.Row = rowIndex;
companyLogo.SetSize(logoFile.Width, logoFile.Height);
//Write all the stuff to the spreadsheet
//Add the workbook to the zip file
zip.AddEntry(excelPackage.Workbook.Properties.Title, excelPackage.GetAsByteArray());
}
}
zip.Save(stream);
return File(stream.ToArray(), System.Net.Mime.MediaTypeNames.Application.Zip, "Project Reports.zip");
}
}
}
Why does the second method return spreadsheets that open with the error "We found a problem with some content ...."??
I currently have a PdfReader and a PdfStamper that I am filling out the acrofields with. I now have to copy another pdf to the end of that form I have been filling out and when I do I lose the acrofield on the new form I copy over. Here is the code.
public static void addSectionThirteenPdf(PdfStamper stamper, Rectangle pageSize, int pageIndex){
PdfReader reader = new PdfReader(FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/resources/documents/Section13.pdf"));
AcroFields fields = reader.getAcroFields();
fields.renameField("SecurityGuidancePage3", "SecurityGuidancePage" + pageIndex);
stamper.insertPage(pageIndex, pageSize);
stamper.replacePage(reader, 1, pageIndex);
}
The way that I am creating the original document is like this.
OutputStream output = FacesContext.getCurrentInstance().getExternalContext().getResponseOutputStream();
PdfReader pdfTemplate = new PdfReader(FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/resources/documents/dd254.pdf"));
PdfStamper stamper = new PdfStamper(pdfTemplate, output);
stamper.setFormFlattening(true);
AcroFields fields = stamper.getAcroFields();
Is there a way to merge using the first piece of code and merge both of the acrofields together?
Depending on what you want exactly, different scenarios are possible, but in any case: you are doing it wrong. You should use either PdfCopy or PdfSmartCopy to merge documents.
The different scenarios are explained in the following video tutorial.
You can find most of the examples in the iText sandbox.
Merging different forms (having different fields)
If you want to merge different forms without flattening them, you should use PdfCopy as is done in the MergeForms example:
public void createPdf(String filename, PdfReader[] readers) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(filename));
copy.setMergeFields();
document.open();
for (PdfReader reader : readers) {
copy.addDocument(reader);
}
document.close();
for (PdfReader reader : readers) {
reader.close();
}
}
In this case, readers is an array of PdfReader instances containing different forms (with different field names), hence we use PdfCopy and we make sure that we don't forget to use the setMergeFields() method, or the fields won't be copied.
Merging identical forms (having identical fields)
In this case, we need to rename the fields, because we probably want different values on different pages. In PDF a field can only have a single value. If you merge identical forms, you have multiple visualizations of the same field, but each visualization will show the same value (because in reality, there is only one field).
Let's take a look at the MergeForms2 example:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
copy.setMergeFields();
document.open();
List<PdfReader> readers = new ArrayList<PdfReader>();
for (int i = 0; i < 3; ) {
PdfReader reader = new PdfReader(renameFields(src, ++i));
readers.add(reader);
copy.addDocument(reader);
}
document.close();
for (PdfReader reader : readers) {
reader.close();
}
}
public byte[] renameFields(String src, int i) throws IOException, DocumentException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.getAcroFields();
Set<String> keys = new HashSet<String>(form.getFields().keySet());
for (String key : keys) {
form.renameField(key, String.format("%s_%d", key, i));
}
stamper.close();
reader.close();
return baos.toByteArray();
}
As you can see, the renameFields() method creates a new document in memory. That document is merged with other documents using PdfSmartCopy. If you'd use PdfCopy here, your document would be bloated (as we'll soon find out).
Merging flattened forms
In the FillFlattenMerge1, we fill out the forms using PdfStamper. The result is a PDF file that is kept in memory and that is merged using PdfCopy. While this example is fine if you'd merge different forms, this is actually an example on how not to do it (as explained in the video tutorial).
The FillFlattenMerge2 shows how to merge identical forms that are filled out and flattened correctly:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
document.open();
ByteArrayOutputStream baos;
PdfReader reader;
PdfStamper stamper;
AcroFields fields;
StringTokenizer tokenizer;
BufferedReader br = new BufferedReader(new FileReader(DATA));
String line = br.readLine();
while ((line = br.readLine()) != null) {
// create a PDF in memory
baos = new ByteArrayOutputStream();
reader = new PdfReader(SRC);
stamper = new PdfStamper(reader, baos);
fields = stamper.getAcroFields();
tokenizer = new StringTokenizer(line, ";");
fields.setField("name", tokenizer.nextToken());
fields.setField("abbr", tokenizer.nextToken());
fields.setField("capital", tokenizer.nextToken());
fields.setField("city", tokenizer.nextToken());
fields.setField("population", tokenizer.nextToken());
fields.setField("surface", tokenizer.nextToken());
fields.setField("timezone1", tokenizer.nextToken());
fields.setField("timezone2", tokenizer.nextToken());
fields.setField("dst", tokenizer.nextToken());
stamper.setFormFlattening(true);
stamper.close();
reader.close();
// add the PDF to PdfCopy
reader = new PdfReader(baos.toByteArray());
copy.addDocument(reader);
reader.close();
}
br.close();
document.close();
}
These are three scenarios. Your question is too unclear for anyone but you to decide which scenario is the best fit for your needs. I suggest that you take the time to learn before you code. Watch the video, try the examples, and if you still have doubts, you'll be able to post a smarter question.
I have the following code which takes an improperly saved Image from the database converts it to a Jpeg and returns the Image in a byte array;
public Byte[] GetImageFromDB(int id)
{
var imageData = _repository.GetImage(id);
var newImageData = ConvertCorruptedImage(imageData, id);
return newImageData;
}
private byte[] ConvertCorruptedImage(byte[] imageData, int id)
{
// Save DB Image as a file.
MemoryStream img = new MemoryStream(imageData);
var saveDBImage = Image.FromStream(img);
string originalFileName = #"c:\original_" + id.ToString() + ".jpg";
string newFileName = #"C:\new" + id.ToString() + ".jpg";
// Delete if already Exists
DeleteImageFile(originalFileName);
saveDBImage.Save(originalFileName);
// Read Saved DB Image From Saved File & Save as jpeg
Bitmap bm = new Bitmap(originalFileName);
bm.Save(newFileName , ImageFormat.Jpeg);
// Return Converted JPEG Image
var newImage = ImageToByte(Image.FromFile(newFileName));
//DeleteCreatedImage(newFileName);
//DeleteCreatedImage(originalFileName);
return newImage;
}
private byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
public static void DeleteImageFile(string fileName)
{
FileInfo file = new FileInfo(fileName);
if (file.Exists && !file.IsReadOnly)
{
System.IO.File.Delete(fileName);
}
}
I was wondering if there was a way to do this without saving a file to the hard disk or if i do save it then deleting it once i am done with it.
I've tried adding a delete for each images (check the commented out portion of the ConvertCorruptedImage method) but i keep getting the following error:
The process cannot access the file 'C:\new_xx.jpg' because it is being used by another process.
I really don't want to be saving images to a hard disk.
Thanks in advance
something along the lines of
var image = Image.FromStream(new MemoryStream(imageData));
Bitmap bmp = new Bitmap(image);
MemoryStream outStream = new MemoryStream();
bmp.Save(outStream,ImageFormat.Jpeg);
return outStream.ToArray();
Use the overload of Bitmap.Save that writes to a Stream.
var stream = new MemoryStream();
bm.Save(stream, ImageFormat.Jpeg);
You can load the bitmap directly from your MemoryStream:
Bitmap bm = new Bitmap(imgStream);
You can also save the bitmap to a stream:
MemoryStream newImgStream = new MemoryStream();
bm.Save(newMemoryStream, ImageFormat.Jpeg);
IN .NET 3.5 I know of the System.ServiceModel.Syndication classes which can create Atom 1.0 and RSS 2.0 rss feeds. Does anyone know of a way to easily generate yahoo's Media RSS in ASP.Net? I am looking for a free and fast way to generate an MRSS feed.
Here a simplified solution I ended up using within a HttpHandler (ashx):
public void GenerateRss(HttpContext context, IEnumerable<Media> medias)
{
context.Response.ContentType = "application/rss+xml";
XNamespace media = "http://search.yahoo.com/mrss";
List<Media> videos2xml = medias.ToList();
XDocument rss = new XDocument(
new XElement("rss", new XAttribute("version", "2.0"),
new XElement("channel",
new XElement("title", ""),
new XElement("link", ""),
new XElement("description", ""),
new XElement("language", ""),
new XElement("pubDate", DateTime.Now.ToString("r")),
new XElement("generator", "XLinq"),
from v in videos2xml
select new XElement("item",
new XElement("title", v.Title.Trim()),
new XElement("link", "",
new XAttribute("rel", "alternate"),
new XAttribute("type", "text/html"),
new XAttribute("href", String.Format("/Details.aspx?vid={0}", v.ID))),
new XElement("id", NotNull(v.ID)),
new XElement("pubDate", v.PublishDate.Value.ToLongDateString()),
new XElement("description",
new XCData(String.Format("<a href='/Details.aspx?vid={1}'> <img src='/Images/ThumbnailHandler.ashx?vid={1}' align='left' width='120' height='90' style='border: 2px solid #B9D3FE;'/></a><p>{0}</p>", v.Description, v.ID))),
new XElement("author", NotNull(v.Owner)),
new XElement("link",
new XAttribute("rel", "enclosure"),
new XAttribute("href", String.Format("/Details.aspx?vid={0}", v.ID)),
new XAttribute("type", "video/wmv")),
new XElement(XName.Get("title", "http://search.yahoo.com/mrss"), v.Title.Trim()),
new XElement(XName.Get("thumbnail", "http://search.yahoo.com/mrss"), "",
new XAttribute("url", String.Format("/Images/ThumbnailHandler.ashx?vid={0}", v.ID)),
new XAttribute("width", "320"),
new XAttribute("height", "240")),
new XElement(XName.Get("content", "http://search.yahoo.com/mrss"), "a",
new XAttribute("url", String.Format("/Details.aspx?vid={0}", v.ID)),
new XAttribute("fileSize", Default(v.FileSize)),
new XAttribute("type", "video/wmv"),
new XAttribute("height", Default(v.Height)),
new XAttribute("width", Default(v.Width))
)
)
)
)
);
using (XmlWriter writer = new XmlTextWriter(context.Response.OutputStream, Encoding.UTF8))
{
try
{
rss.WriteTo(writer);
}
catch (Exception ex)
{
Log.LogError("VideoRss", "GenerateRss", ex);
}
}
}
Step 1: Convert your data into XML:
So, given a List photos:
var photoXml = new XElement("photos",
new XElement("album",
new XAttribute("albumId", albumId),
new XAttribute("albumName", albumName),
new XAttribute("modified", DateTime.Now.ToUniversalTime().ToString("r")),
from p in photos
select
new XElement("photo",
new XElement("id", p.PhotoID),
new XElement("caption", p.Caption),
new XElement("tags", p.StringTags),
new XElement("albumId", p.AlbumID),
new XElement("albumName", p.AlbumName)
) // Close element photo
) // Close element album
);// Close element photos
Step 2: Run the XML through some XSLT:
Then using something like the following, run that through some XSLT, where xslPath is the path to your XSLT, current is the current HttpContext:
var xt = new XslCompiledTransform();
xt.Load(xslPath);
var ms = new MemoryStream();
if (null != current){
var xslArgs = new XsltArgumentList();
var xh = new XslHelpers(current);
xslArgs.AddExtensionObject("urn:xh", xh);
xt.Transform(photoXml.CreateNavigator(), xslArgs, ms);
} else {
xt.Transform(photoXml.CreateNavigator(), null, ms);
}
// Set the position to the beginning of the stream.
ms.Seek(0, SeekOrigin.Begin);
// Read the bytes from the stream.
var byteArray = new byte[ms.Length];
ms.Read(byteArray, 0, byteArray.Length);
// Decode the byte array into a char array
var uniEncoding = new UTF8Encoding();
var charArray = new char[uniEncoding.GetCharCount(
byteArray, 0, byteArray.Length)];
uniEncoding.GetDecoder().GetChars(
byteArray, 0, byteArray.Length, charArray, 0);
var sb = new StringBuilder();
sb.Append(charArray);
// Returns the XML as a string
return sb.ToString();
I have those two bits of code sitting in one method "BuildPhotoStream".
The class "XslHelpers" contains the following:
public class XslHelpers{
private readonly HttpContext current;
public XslHelpers(HttpContext currentContext){
current = currentContext;
}
public String ConvertDateTo822(string dateTime){
DateTime original = DateTime.Parse(dateTime);
return original.ToUniversalTime()
.ToString("ddd, dd MMM yyyy HH:mm:ss G\"M\"T");
}
public String ServerName(){
return current.Request.ServerVariables["Server_Name"];
}
}
This basically provides me with some nice formatting of dates that XSLT doens't give me.
Step 3: Render the resulting XML back to the client application:
"BuildPhotoStream" is called by "RenderHelpers.Photos" and "RenderHelpers.LatestPhotos", which are responsible for getting the photo details from the Linq2Sql objects, and they are called from an empty aspx page (I know now that this should really be an ASHX handler, I've just not gotten around to fixing it):
protected void Page_Load(object sender, EventArgs e)
{
Response.ContentType = "application/rss+xml";
ResponseEncoding = "UTF-8";
if (!string.IsNullOrEmpty(Request.QueryString["AlbumID"]))
{
Controls.Add(new LiteralControl(RenderHelpers
.Photos(Server.MapPath("/xsl/rssPhotos.xslt"), Context)));
}
else
{
Controls.Add(new LiteralControl(RenderHelpers
.LatestPhotos(Server.MapPath("/xsl/rssLatestPhotos.xslt"), Context)));
}
}
At the end of all that, I end up with this:
http://www.doodle.co.uk/Albums/Rss.aspx?AlbumID=61
Which worked in Cooliris/PicLens when I set it up, however now seems to render the images in the reflection plane, and when you click on them, but not in the wall view :(
In case you missed it above, the XSLT can be found here:
http://www.doodle.co.uk/xsl/rssPhotos.xslt
You'll obviously need to edit it to suit your needs (and open it in something like Visual Studio - FF hides most of the stylesheet def, including xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss").
I was able to add the media namespace to the rss tag by doing the following:
XmlDocument feedDoc = new XmlDocument();
feedDoc.Load(new StringReader(xmlText));
XmlNode rssNode = feedDoc.DocumentElement;
// You cant directly set the attribute to anything other then then next line. So you have to set the attribute value on a seperate line.
XmlAttribute mediaAttribute = feedDoc.CreateAttribute("xmlns", "media", "http://www.w3.org/2000/xmlns/");
mediaAttribute.Value = "http://search.yahoo.com/mrss/";
rssNode.Attributes.Append(mediaAttribute);
return feedDoc.OuterXml;
Just to add another option for future reference:
I created a library that leverages the SyndicationFeed classes in .NET and allows you to read and write media rss feeds.
http://mediarss.codeplex.com