SSRS Report in C# console app doesn't fill printed page - c#

Below is code for a VS2015 C# console app where I printing an 8.5x14 SSRS report (no data connection). When I print the report from Report Builder, it renders correctly. When I print it from my console app, the report doesn't fill the page. I am rendering the report to a stream where I set the size to 8.5x14, then printing it, setting the document size to 8.5x14. Not sure what else I need to do to get it to fit.
If anyone would take a look and offer advise, I would be very grateful.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using Microsoft.Reporting.WinForms;
namespace SULabelPrinting
{
class Program
{
private static int m_currentPageIndex;
private static IList<Stream> m_streams;
static void Main()
{
try
{
LocalReport report = new LocalReport();
report.ReportPath = #"..\..\Test.rdl";
Export(report);
Print();
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
// Routine to provide to the report renderer, in order to
// save an image for each page of the report.
private static Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
// Export the given report as an EMF (Enhanced Metafile) file.
private static void Export(LocalReport report)
{
string deviceInfo =
#"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>14in</PageWidth>
<PageHeight>8.5in</PageHeight>
<MarginTop>.1in</MarginTop>
<MarginLeft>.1in</MarginLeft>
<MarginRight>.1in</MarginRight>
<MarginBottom>.1in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
// Handler for PrintPageEvents
private static void PrintPageHandler(object sender, PrintPageEventArgs ev)
{
Metafile pageImage = new
Metafile(m_streams[m_currentPageIndex]);
// Adjust rectangular area with printer margins.
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int) ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int) ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// Draw a white background for the report
ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
// Draw the report content
ev.Graphics.DrawImage(pageImage, adjustedRect);
// Prepare for the next page. Make sure we haven't hit the end.
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
private static void Print()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("No stream to print.");
PrintDocument printDoc = new PrintDocument();
printDoc.DefaultPageSettings.PaperSize = new PaperSize("Legal", 850, 1400);
printDoc.DefaultPageSettings.Landscape = true;
printDoc.PrintPage += new PrintPageEventHandler(PrintPageHandler);
m_currentPageIndex = 0;
printDoc.Print();
}
public void Dispose()
{
if (m_streams != null)
{
foreach (Stream stream in m_streams)
stream.Close();
m_streams = null;
}
}
}
}
If it helps, below is a link to the project.
SULabelPrinting.zip
Thanks,
Steve.

Related

Word Interop - Save embedded shape as image

I am attempting to save an embedded shape as an image using C#.
If the object is embedded as an actual image (WMF/JPEG) I can retrieve the image without issue but when the object is an embedded shape or an OLE Object that displays as an image in Word I cannot seem to extract or retrieve said object to then either copy to the clipboard or save said image.
Here is my current code sample; either the object is empty or I get the following error:
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
Any help is appreciated. Thank you
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ImageMagickSandboxWinForms
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public static BitmapSource ConvertBitmap(Bitmap source)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
source.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
private void button1_Click(object sender, EventArgs e)
{
string physicsDocLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
physicsDocLocation += #"\[Doc path Here].docx";
var wordApp = new Microsoft.Office.Interop.Word.Application();
var wordDoc = wordApp.Documents.Open(physicsDocLocation);
int iCount = wordDoc.InlineShapes.Count;
for (int i = 1; i < (wordDoc.InlineShapes.Count + 1); i++)
{
var currentInlineShape = wordDoc.InlineShapes[i];
currentInlineShape.Range.Select();
wordDoc.ActiveWindow.Selection.Range.Copy();
BitmapSource clipBoardImage = System.Windows.Clipboard.GetImage();
Bitmap bmpClipImage = BitmapFromSource(clipBoardImage);
string finalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), #"TestConversions");
finalPath += #"\" + Guid.NewGuid().ToString() + ".jpg";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(finalPath, FileMode.Create, FileAccess.ReadWrite))
{
bmpClipImage.Save(memory, ImageFormat.Jpeg); <<<---- Error happens here.
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
}
wordDoc.Close();
wordApp.Quit();
}
}
}
i have these code in my library, dunno where i have found that but hope you do the job for you: i am using Clippboard to trap the different images, jus t dont forget, Thread is needed to access Clipboard
for (var i = 1; i <= wordApplication.ActiveDocument.InlineShapes.Count; i++)
{
var inlineShapeId = i;
var thread = new Thread(() => SaveInlineShapeToFile(inlineShapeId, wordApplication));
// STA is needed in order to access the clipboard
// https://stackoverflow.com/a/518724/700926
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
// General idea is based on: https://stackoverflow.com/a/7937590/700926
protected static void SaveInlineShapeToFile(int inlineShapeId, Application wordApplication)
{
// Get the shape, select, and copy it to the clipboard
var inlineShape = wordApplication.ActiveDocument.InlineShapes[inlineShapeId];
inlineShape.Select();
wordApplication.Selection.Copy();
// Check data is in the clipboard
if (Clipboard.GetDataObject() != null)
{
var data = Clipboard.GetDataObject();
// Check if the data conforms to a bitmap format
if (data != null && data.GetDataPresent(DataFormats.Bitmap))
{
// Fetch the image and convert it to a Bitmap
var image = (Image) data.GetData(DataFormats.Bitmap, true);
var currentBitmap = new Bitmap(image);
// Save the bitmap to a file
currentBitmap.Save(#"C:\Users\Username\Documents\" + String.Format("img_{0}.png", inlineShapeId));
}
}
}
following if you are using Winform or WPF the clipboard acts differently for an image:
if (Clipboard.ContainsImage())
{
// ImageUIElement.Source = Clipboard.GetImage(); // does not work
System.Windows.Forms.IDataObject clipboardData = System.Windows.Forms.Clipboard.GetDataObject();
if (clipboardData != null)
{
if (clipboardData.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
{
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)clipboardData.GetData(System.Windows.Forms.DataFormats.Bitmap);
ImageUIElement.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());
Console.WriteLine("Clipboard copied to UIElement");
}
}
}
after if its not functionam due to a bug in translation of format, there is this solution . So its infrecnh but its easily to understand the logic of the using of "DeviceIndependentBitmap"

Printing RDLC to a pre-defined printer without print preview

In my C# winform app, I need to print an rdlc report directly to a printer which is not default, so I will need to specify the printer name/path and send it directly, can anyone refer to a good example? all what i found so far prints directly to the default printer, I will need to define the printer before printing the rdlc report.
the reason for this is because I have 3 printers connected to the PC, a bar printer, receipt printer, and kitchen printer. the receipt printer is the default one. so sending rdlc report to it is pretty easy as it's default, my concern is how to send the report to the kitchen and bar printers
Thanks
Add a class named LocalReportExtension.cs to your project:
using Microsoft.Reporting.WinForms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
public static class LocalReportExtensions
{
public static void Print(this LocalReport report, string printerName)
{
var pageSettings = new PageSettings();
pageSettings.PaperSize = report.GetDefaultPageSettings().PaperSize;
pageSettings.Landscape = report.GetDefaultPageSettings().IsLandscape;
pageSettings.Margins = report.GetDefaultPageSettings().Margins;
Print(report, pageSettings, printerName);
}
public static void Print(this LocalReport report, PageSettings pageSettings, string printerName)
{
string deviceInfo =
$#"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>{pageSettings.PaperSize.Width * 100}in</PageWidth>
<PageHeight>{pageSettings.PaperSize.Height * 100}in</PageHeight>
<MarginTop>{pageSettings.Margins.Top * 100}in</MarginTop>
<MarginLeft>{pageSettings.Margins.Left * 100}in</MarginLeft>
<MarginRight>{pageSettings.Margins.Right * 100}in</MarginRight>
<MarginBottom>{pageSettings.Margins.Bottom * 100}in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
var streams = new List<Stream>();
var currentPageIndex = 0;
report.Render("Image", deviceInfo,
(name, fileNameExtension, encoding, mimeType, willSeek) =>
{
var stream = new MemoryStream();
streams.Add(stream);
return stream;
}, out warnings);
foreach (Stream stream in streams)
stream.Position = 0;
if (streams == null || streams.Count == 0)
throw new Exception("Error: no stream to print.");
var printDocument = new PrintDocument();
printDocument.DefaultPageSettings = pageSettings;
if (!printDocument.PrinterSettings.IsValid)
throw new Exception("Error: cannot find the default printer.");
else
{
printDocument.PrintPage += (sender, e) =>
{
Metafile pageImage = new Metafile(streams[currentPageIndex]);
Rectangle adjustedRect = new Rectangle(
e.PageBounds.Left - (int)e.PageSettings.HardMarginX,
e.PageBounds.Top - (int)e.PageSettings.HardMarginY,
e.PageBounds.Width,
e.PageBounds.Height);
e.Graphics.FillRectangle(Brushes.White, adjustedRect);
e.Graphics.DrawImage(pageImage, adjustedRect);
currentPageIndex++;
e.HasMorePages = (currentPageIndex < streams.Count);
e.Graphics.DrawRectangle(Pens.Red, adjustedRect);
};
printDocument.EndPrint += (Sender, e) =>
{
if (streams != null)
{
foreach (Stream stream in streams)
stream.Close();
streams = null;
}
};
printDocument.PrinterSettings.PrinterName = printerName;
printDocument.Print();
}
}
}
Then call your reportviewr (passing the printer name as a parameter) like this:
this.reportViewer.LocalReport.Print("\\YourServer\YourPrinterName");

Convert Tiff to pdf with PDFSharp-gdi C#

I am stuck. Currently I am trying to simulate pulling a binary blob from a database that is supposed to be a TIFF image. I use this gist image.tif in the image variable to do so. I am pretty sure I am close to making this happen. It's that the issue probably has to do with how I am converting the string to byte array or something. Basically this application throws an exception stating that it can't create a PDF with 0 frames. At this point I must admit that I may be in over my head on this one. Could someone be so kind and help me the rest of the way with this one?
The code is included below:
using System;
using System.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Text;
namespace ConvertTifToPDFFile
{
class Program
{
static void Main(string[] args)
{
string image = "";
byte[] imageAsByteStream = Encoding.ASCII.GetBytes(image);
int imageByteStreamLength = imageAsByteStream.Length;
string base64EncodedImage = Convert.ToBase64String(imageAsByteStream);
imageAsByteStream = Encoding.ASCII.GetBytes(base64EncodedImage);
Stream imageStream = TiffImageSplitter.ByteArrayToMemoryStream(imageAsByteStream);
// Image splitImage = TiffImageSplitter.getTiffImage(imageStream, 1);
TiffImageSplitter.tiff2PDF(imageStream);
}
}
public class TiffImageSplitter
{
private static TiffImageSplitter tiff = new TiffImageSplitter();
public static void tiff2PDF(Stream imageByteStream)
{
PdfDocument doc = new PdfDocument();
int pageCount = getPageCount(imageByteStream);
for (int i = 0; i < pageCount; i++)
{
PdfPage page = new PdfPage();
Image img = getTiffImage(imageByteStream, 1);
XImage imgFrame = XImage.FromGdiPlusImage(img);
page.Width = imgFrame.PointWidth;
page.Height = imgFrame.PointHeight;
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[i]);
xgr.DrawImage(img, 0, 0);
}
doc.Save("C:/temp/test.pdf");
doc.Close();
}
public static Image getTiffImage(Stream imageStream, int pageNumber)
{
MemoryStream ms = null;
Image returnImage = null;
try
{
ms = new MemoryStream();
Image sourceImage = Image.FromStream(imageStream, true, true);
Guid objGuid = sourceImage.FrameDimensionsList[0];
FrameDimension objDimension = new FrameDimension(objGuid);
sourceImage.SelectActiveFrame(objDimension, pageNumber);
sourceImage.Save(ms, ImageFormat.Tiff);
returnImage = Image.FromStream(ms);
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
returnImage = null;
}
return returnImage;
}
public static MemoryStream ByteArrayToMemoryStream(byte[] bytestream)
{
MemoryStream stream = new MemoryStream();
stream.Write(bytestream, 0, bytestream.Length);
return stream;
}
public static int getPageCount(Stream imageStream)
{
int pageCount = -1;
try
{
Image img = Image.FromStream(imageStream, true, true);
pageCount = img.GetFrameCount(FrameDimension.Page);
img.Dispose();
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
pageCount = 0;
}
return pageCount;
}
}
}
IMPORTANT!!!! First of all, your example tiff isn't valid at all. It can't be read by any file editor. I have had to take these ones for testing.
Then, the code has couple errors:
1) I don't understand what've done with strings but reading files and BLOBs are same:
static void Main(string[] args)
{
//string image = "";
//byte[] imageAsByteStream = Encoding.ASCII.GetBytes(image);
byte[] imageAsByteStream = File.ReadAllBytes("../../../MARBIBM.TIF");
//int imageByteStreamLength = imageAsByteStream.Length;
//string base64EncodedImage = Convert.ToBase64String(imageAsByteStream);
//imageAsByteStream = Encoding.ASCII.GetBytes(base64EncodedImage);
Stream imageStream = TiffImageSplitter.ByteArrayToMemoryStream(imageAsByteStream);
// Image splitImage = TiffImageSplitter.getTiffImage(imageStream, 1);
TiffImageSplitter.tiff2PDF(imageStream);
}
2) method tiff2PDF should be like that
public static void tiff2PDF(Stream imageByteStream)
{
PdfDocument doc = new PdfDocument();
int pageCount = getPageCount(imageByteStream);
for (int i = 0; i < pageCount; i++)
{
PdfPage page = new PdfPage();
Image img = getTiffImage(imageByteStream, i); //<---HERE WAS ANOTHER ERROR, LOOK AT i
XImage imgFrame = XImage.FromGdiPlusImage(img);
3)
public static MemoryStream ByteArrayToMemoryStream(byte[] bytestream)
{
MemoryStream stream = new MemoryStream(bytestream);
//stream.Write(bytestream, 0, bytestream.Length);
return stream;
}

Can't retrieve image from SQL Server using Generic Handler c#

Whenever I retrieve an image using my Generic Handler, I retrieve either an empty image or a broken image.
Here is my code.
aspx File:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//imports
using DHELTASSys.Modules;
using System.Data;
using DHELTASSys.AuditTrail;
namespace DHELTASSys
{
public partial class EvaluateOffense : System.Web.UI.Page
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
DHELTASSysAuditTrail audit = new DHELTASSysAuditTrail();
protected void Page_Load(object sender, EventArgs e)
{
string position = Session["Position"].ToString();
if (Session["EmployeeID"] == null)
{
Response.Redirect("LogIn.aspx");
} else if(position != "HR Manager")
{
Response.Redirect("AccessDenied.aspx");
}
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
DataTable dt = discipline.GetProof();
if (dt.Rows == null)
{
Label9.Visible = false;
Image1.Visible = false;
}
}
protected void btnEvaluate_Click(object sender, EventArgs e)
{
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
discipline.Decision = drpDecision.Text;
discipline.AddOffenseDecision();
audit.Emp_id = int.Parse(Session["EmployeeID"].ToString());
audit.AddAuditTrail(drpDecision.Text + "ed Employee's offense.");
}
}
}
Here is the handler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
//imports
using System.Data;
using DHELTASSys.Modules;
using DHELTASSys.DataAccess;
namespace DHELTASSys
{
public class ShowImage : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
public void ProcessRequest(HttpContext context)
{
if (context.Session["OffenseID"].ToString() == null) return;
int offense_emp_id = int.Parse(context.Session["OffenseID"].ToString());
discipline.Offense_emp_id = offense_emp_id;
DataTable dt = discipline.GetProof();
if (dt.Rows == null) return;
int id = 1;
string image = dt.Rows[0][1].ToString() + id;
string FileName = dt.Rows[0][0].ToString();
string FileContentType = dt.Rows[0][2].ToString();
Byte[] bytes = (Byte[])dt.Rows[0][1];
string imageBase64 = Convert.ToBase64String(bytes);
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
MemoryStream ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
context.Response.Buffer = true;
System.Drawing.Image imagen = System.Drawing.Image.FromStream(ms);
context.Response.BinaryWrite(bytes);
ms.Dispose();
}
else
{
return;
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
And adding to that, here is my image object.
<asp:Image ID="Image1" runat="server" ImageUrl="~/ShowImage.ashx" />
I already tweaked my code in so many ways.
The image file is stored in SQL Server using the data type "Image"
As you can see, I'm using the session to retrieve the specified image from the Database.
I have no problem in accessing the session whatsoever.
Thanks in advance.
Your code seems a bit more complicated than necessary; you can write the binary data to the outputstream without needing to load it into a memory stream, and you're doing nothing with your System.Drawing.Image object. Try this:
context.Response.OutputStream.Write(bytes, 0, bytes.Length);
First, I don't see anything wrong with the way you are writing the image to the response stream. There's a few useless lines of code but I understand that as you said, you were tweaking your code in desperation. Basically, this should be good enough...
Byte[] bytes = (Byte[])dt.Rows[0][1];
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
context.Response.BinaryWrite(bytes);
context.ApplicationInstance.CompleteRequest(); //just to make sure the ASP.NET pipeline completes the request
}
else
{
return;
}
Now, that "should" work given that bytes has been correctly casted to a byte array. Make sure to add some proper exception handling and logging because I'm a bit suspicious that the problem it's somewhere else. So, follow these steps:
Clean up your code for better clarity
Debug the app
Implement some exception handling
Implement some sort of error logging
You should be able to get to the bottom of this issue since there's nothing wrong in sending an array of bytes using the BinaryWrite method
This is real example that pickup from my project
into page.cs set image url dynamically:
// for diffrent url using guid in url
imgProfilePic.ImageUrl = "GenericHandler_ShowImage.ashx?Ref=" + Guid.NewGuid().ToString() + "&n=" + lngEmployeeID;
into handler, get image by table adapter
// in handler
public void ProcessRequest(HttpContext context)
{
long EmployeeID = -1;
if (context.Request.QueryString["n"] != null)
EmployeeID = long.Parse(context.Request.QueryString["n"].ToString());
//else
// throw new ArgumentException("No parameter specified");
if (context.Request.QueryString["actid"] == "2")
{
ShowThumbPic(context, EmployeeID);
return;
}
//else ...
}
private void ShowThumbPic(HttpContext context, long EmployeeID)
{
context.Response.ContentType = "image/jpeg";
Stream myStream = GetEmpImage(EmployeeID);
byte[] myImgByteArray;
using (BinaryReader br = new BinaryReader(myStream))
{
myImgByteArray = br.ReadBytes((int)myStream.Length);
}
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
if (img.Height > 50 || img.Width > 50)
{
System.Drawing.Size siz = GetScaledSize(img.Size, new System.Drawing.Size(50, 50));
img = (System.Drawing.Image)ResizeImage(img, siz);
}
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] myBuffer = ms.ToArray();
int byteSeq = myBuffer.Length; //myStream.Read(myBuffer, 0, 4096);
if (byteSeq > 0)
context.Response.OutputStream.Write(myBuffer, 0, byteSeq);
}
private System.Drawing.Image GetEmployeeImage(long EmployeeID)
{
System.Drawing.Image img = null;
try
{
using (DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter ta = new DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter())
{
object obj = ta.spr_Employee_Info_GetPicture(EmployeeID);
if (obj != null)
{
MemoryStream ms = new MemoryStream((byte[])obj);
img = System.Drawing.Image.FromStream(ms);
}
}
}
catch (Exception x)
{
throw new Exception(Msg.Error_InDownloadPicture + x.Message);
}
return img;
}
public static Size GetScaledSize(Size ImageSize, Size FrameSize)
{
int newWidth = ImageSize.Width;
int newHeight = ImageSize.Height;
double ratioX = (double)FrameSize.Width / ImageSize.Width;
double ratioY = (double)FrameSize.Height / ImageSize.Height;
double ratio = Math.Min(ratioX, ratioY);
if (ratio < 1.0f) // if Frame is greater than image, resize it.
{
newWidth = (int)(ImageSize.Width * ratio);
newHeight = (int)(ImageSize.Height * ratio);
}
return new Size(newWidth, newHeight);
}
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, Size siz)
{
//a holder for the result
Bitmap result = new Bitmap(siz.Width, siz.Height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}

Multipage PDF document from predefined template

I have a requirement to generate invoice reports in PDF using some predefined company templates. I am able to create/generate SINGLE PAGE PDF reports using iTextSharp.
Problem: The problem comes when the invoice statement spans MULTIPLE PAGES. I am not able to extend the report(invoice statement) to next(2nd) page. If all the data can not be accommodated on one page it should be written on 2nd page, while still using the company template.
The template is present at following path:
HostingEnvironment.MapPath("~/Content/InvoiceTemplate/invoiceTemplate.pdf")
I am using iTextSharp library to create documents. Below is the code used to generate the PDF:
public class pdfStatementController : Controller {
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
//
// GET: /pdfStatement/
public ActionResult SendPdfStatement(string InvoiceNumber) {
try {
InvoiceNumber = InvoiceNumber.Trim();
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
List<Models.Statement> statementList = new List<Models.Statement>();
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
pdfStatementController.WriteInTemplate(statementList);
return RedirectToAction("Invoice", "Invoice", new { id = statementList.FirstOrDefault().Customer_ID.ToString().Trim() });
} catch (Exception e) {
return View("Error");
}
}
public static void WriteInTemplate(List<Models.Statement> statementList) {
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
string month = null;
string day = null;
string year = null;
PdfReader pdfReader = new PdfReader(
HostingEnvironment.MapPath(
"~/Content/InvoiceTemplate/invoiceTemplate.pdf"));
FileStream fileStream = new FileStream(
HostingEnvironment.MapPath(
"~/Content/reports/" + invoiceNumber + ".pdf"),
FileMode.Create);
PdfStamper pdfStamper = new PdfStamper(pdfReader, fileStream);
AcroFields pdfFields = pdfStamper.AcroFields;
pdfFields.SetField("BillToCompany", statementList.FirstOrDefault().BillToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("BillToContact", statementList.FirstOrDefault().BillToContact.ToString().Trim().ToUpper());
pdfFields.SetField("CustomerId", statementList.FirstOrDefault().Customer_ID);
pdfFields.SetField("InvoiceNumber", statementList.FirstOrDefault().Invoice.ToString().Trim());
pdfFields.SetField("JobNumber", statementList.FirstOrDefault().JobNumber.ToString().Trim());
pdfFields.SetField("Caller", statementList.FirstOrDefault().Caller.ToString().Trim());
pdfStamper.FormFlattening = true; // generate a flat PDF
pdfStamper.Close();
pdfReader.Close();
}
}
Your code looks good and is only missing a couple of intermediate steps.
Since you're using the same PDF template for every page (when two or more pages need to be generated), instead of using a PdfStamper to add content directly to the Document, you use a PdfSmartCopy or PdfCopy object.
The PdfStamper is still needed. However, in this case it's used to create an in-memory (single) page filled with data as you as you iterate over your Models.Statement collection.
In other words, PdfSmartCopy/PdfCopy maintains your statements as a whole, (total pages) and PdfStamper is used as a buffer that adds your individual statements page-by-page to your PDF. Here's a simple working example HTTP hander (.ashx):
<%# WebHandler Language="C#" Class="copyFillTemplate" %>
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using iTextSharp.text;
using iTextSharp.text.pdf;
public class copyFillTemplate : IHttpHandler {
public void ProcessRequest (HttpContext context) {
HttpServerUtility Server = context.Server;
HttpResponse Response = context.Response;
Response.ContentType = "application/pdf";
// template used to test __this__ example;
// replace with __your__ PDF template
string pdfTemplatePath = Server.MapPath(
"~/app_data/template.pdf"
);
// this example's test data; replace with __your__ data collection
List<Statement> statementList = Statement.GetStatements();
// COPY FROM HERE
using (Document document = new Document()) {
// PdfSmartCopy reduces PDF file size by reusing parts
// of the PDF template, but uses more memory. you can
// replace PdfSmartCopy with PdfCopy if memory is an issue
using (PdfSmartCopy copy = new PdfSmartCopy(
document, Response.OutputStream)
)
{
document.Open();
// used to test this example
int counter = 0;
// generate one page per statement
foreach (Statement statment in statementList) {
++counter;
// replace this with your PDF form template
PdfReader reader = new PdfReader(pdfTemplatePath);
using (var ms = new MemoryStream()) {
using (PdfStamper stamper = new PdfStamper(reader, ms)) {
AcroFields form = stamper.AcroFields;
// replace this with your field data for each page
form.SetField("title", counter.ToString());
stamper.FormFlattening = true;
}
reader = new PdfReader(ms.ToArray());
// add one page at a time; assumes your template is only one page.
// if your template is more than one page you will need to
// call GetImportedPage() for each page in your template
copy.AddPage(copy.GetImportedPage(reader, 1));
}
}
}
// COPY TO HERE
}
}
public bool IsReusable { get { return false; } }
public class Statement {
public string FieldName, FieldValue;
public static List<Statement> GetStatements() {
List<Statement> s = new List<Statement>();
for (int i = 0; i < 5; ++i) {s.Add(new Statement());}
return s;
}
}
}
Hopefully the inline comments help. And you obviously need to remove replace some parts I used to test the example code.
Though the last answer is a very good one and helped me to solve my problem, i am putting it here to sum up the question.
Problem: I had a scenario to generate multipage pdf document in Company provided template. Needed to generate invoice statements and attach them to email via Microsoft outlook email Client.
I use MVC3, ASP.NET 4.0, Entity Framework
Solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Data;
using System.Data.Objects;
using System.IO;
using iTextSharp;
using iTextSharp.text;
using iTextSharp.text.html;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
namespace InvoiceSearchTool.Controllers
{
public class pdfStatementController : Controller
{
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
//
// GET: /pdfStatement/
public ActionResult SendPdfStatement(string InvoiceNumber)
{
try
{
InvoiceNumber = InvoiceNumber.Trim();
List<Models.Statement> statementList = new List<Models.Statement>();
//this is if you use entity framework
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
}
//others can simply use line like
//statementList = GetStatementList(inviceNumber);
pdfStatementController.WriteInTemplate(statementList);
return RedirectToAction("Invoice", "Invoice", new { id = statementList.FirstOrDefault().Customer_ID.ToString().Trim() });
}
catch (Exception e)
{
return View("Error");
}
}
public static void WriteInTemplate(List<Models.Statement> statementList)
{
try
{
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
using (Document document = new Document())
{
FileStream fileStream = new FileStream(HostingEnvironment.MapPath("~/Content/reports/" + invoiceNumber + ".pdf"), FileMode.Create);
using (PdfSmartCopy smartCopy = new PdfSmartCopy(document, fileStream))
{
document.Open();
PdfReader pdfReader = new PdfReader(HostingEnvironment.MapPath("~/Content/InvoiceTemplate/invoiceTemplate.pdf"));
using (var memoryStream = new MemoryStream())
{
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, memoryStream))
{
string month = null;
string day = null;
string year = null;
AcroFields pdfFields = pdfStamper.AcroFields;
{//billing address
pdfFields.SetField("BillToCompany", statementList.FirstOrDefault().BillToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("BillToContact", statementList.FirstOrDefault().BillToContact.ToString().Trim().ToUpper());
pdfFields.SetField("ShipToCompany", statementList.FirstOrDefault().ShipToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("ShipToContact", statementList.FirstOrDefault().ShipToContact.ToString().Trim().ToUpper());
pdfFields.SetField("PONumber", statementList.FirstOrDefault().PurchaseOrderNo.ToString().Trim());
pdfFields.SetField("OrderNumber", statementList.FirstOrDefault().Order_Number.ToString().Trim());
pdfFields.SetField("ShippingMethod", statementList.FirstOrDefault().Shipping_Method.ToString().Trim());
pdfFields.SetField("PaymentTerms", statementList.FirstOrDefault().Payment_Terms.ToString().Trim());
}
pdfStamper.FormFlattening = true; // generate a flat PDF
}
pdfReader = new PdfReader(memoryStream.ToArray());
smartCopy.AddPage(smartCopy.GetImportedPage(pdfReader, 1));
}
}
}
emailController.CreateMessageWithAttachment(invoiceNumber);
}
catch (Exception e)
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Net;
using System.Net.Mail;
using System.Web.Hosting;
using System.Net.NetworkInformation;
using System.Data.Objects;
namespace InvoiceSearchTool.Controllers
{
public class emailController : Controller
{
//
// GET: /email/
public static void CreateMessageWithAttachment(string invoiceNumber)
{
try
{
Outlook.Application oApp = new Outlook.Application();
Outlook.MailItem email = (Outlook.MailItem)(oApp.CreateItem(Outlook.OlItemType.olMailItem));
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
string recipient = null;
string messageBody = null;
#region set email recipients
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", invoiceNumber);
List<Models.EmailAddress> emailList = _db.ExecuteFunction<Models.EmailAddress>("uspGetEmailAddress", parameters).ToList<Models.EmailAddress>();
if(!string.IsNullOrEmpty(emailList[0].Email.ToString()))
recipient = emailList[0].Email.ToString().Trim();
else
recipient = " ";
email.Recipients.Add(recipient);
}
#endregion
//email subject
email.Subject = "Invoice # " + invoiceNumber;
#region set email Text
{
Models.EmailText emailText = _db.ExecuteFunction<Models.EmailText>("uspEmailText").SingleOrDefault();
messageBody = emailText.EmailTextLine1.ToString().Trim() + "\n\n\n\n\n\n\n\n\n";
messageBody += emailText.EmailTextLine2.ToString().Trim() + "\n";
messageBody += emailText.EmailTextLine3.ToString().Trim();
email.Body = messageBody;
}
#endregion
#region email attachment
{
string fileName = invoiceNumber.Trim();
string filePath = HostingEnvironment.MapPath("~/Content/reports/");
filePath = filePath + fileName + ".pdf";
fileName += ".pdf";
int iPosition = (int)email.Body.Length + 1;
int iAttachType = (int)Outlook.OlAttachmentType.olByValue;
Outlook.Attachment oAttach = email.Attachments.Add(filePath, iAttachType, iPosition, fileName);
}
#endregion
email.Display();
//uncomment below line to SendAutomatedEmail emails atomaticallly
//((Outlook.MailItem)email).Send();
}
catch (Exception e)
{
}
}

Categories

Resources