Im trying to allow users to save/view a gantt chart using PDF Sharp, my problem is when a user selects too many rows the image gets compressed and is unreadable, is there a way i can allow/set the image to stretch across multiple pages?
So im creating two images combining them then outputting to a pdf page, I have tried setting the page width to a higher but this didnt work.
Bitmap bitmap = new Bitmap(image.Width + imageTest.Width, Math.Max(image.Height, imageTest.Height));
using (Graphics combineG = Graphics.FromImage(bitmap))
{
combineG.DrawImage(imageTest, 0, 0);
combineG.DrawImage(image, imageTest.Width, 0);
}
//convert to pdf
PdfDocument document = new PdfDocument();
document.Info.Title = "Holiday_Year " + year + "&Quarter " + quarter;
PdfPage page = document.AddPage();
page.Orientation = PdfSharp.PageOrientation.Landscape;
XGraphics gfx = XGraphics.FromPdfPage(page);
XFont font = new XFont("Verdana", 20, XFontStyle.BoldItalic);
gfx.DrawImage(bitmap, new XRect(0, 100, page.Width, 100));
gfx.DrawString("Holiday - Quarter " + quarter + " & Year " + year, font, XBrushes.Black, new XRect(0, 0, page.Width, 40), XStringFormats.Center);
byte[] fileContents = null;
MemoryStream memoryStream = new MemoryStream();
document.Save(memoryStream, true);
fileContents = memoryStream.ToArray();
memoryStream.Close();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=IEMS-Holiday.pdf");
Response.BinaryWrite(fileContents);
Response.End();
To answer the question: PDFsharp has no built-in feature that allows images to spread across multiple pages.
An image can be drawn on several pages at different positions, displaying different parts of the image. After printing, these pages could be stitched together. The image will be included in the PDF file only once
PDFsharp supports custom page sizes.
I think i have sort of resolved my issue, i added in this code to check the image height, then output to the correct page size.
PdfPage page = document.AddPage();
if (image.Height > 1000)
{
page.Size = PageSize.A1;
}
else
{
page.Size = PageSize.A2;
}
this way it put a large image onto a A1 page etc, then i let adobe figure out how to print the page, seems to work well on viewing and printing
Took me a little while to get the numbers right, but here is what I got and seems to take a single image that is as long as you need it and cuts it up into smaller chunks, placing each chunk on their own page. Of course you will need to adjust the width depending on how much your doing, but again, it's just playing with the numbers.
PdfDocument doc = new PdfDocument();
XImage img = XImage.FromFile(ImgPath);
double captureHeight = 610;
double captureWidth = 790;
double totalHeight = img.PixelHeight / 1.5;
double totalWidth = img.PixelWidth;
PdfPage page = null;
int i = 0;
double saveHeight = 0;
while (saveHeight < totalHeight)
{
page = new PdfPage();
page.Size = PdfSharp.PageSize.Letter;
page.Orientation = PdfSharp.PageOrientation.Landscape;
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[i]);
xgr.DrawImage(img, 0, (-i * captureHeight), captureWidth, totalHeight);
saveHeight += captureHeight;
i++;
}
doc.Save(PdfPath);
doc.Close();
Related
I am trying to make a orders page and there i have added a button which fetches some data and i am trying to display that data in pdf file and generate a pdf file. the data is being displayed correctly but i cant get the watermark on pdf.
here's the code
using (MemoryStream stream = new MemoryStream())
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
PdfDocument document = new PdfDocument();
PdfPage page = document.AddPage();
XFont font = new XFont("Verdana", 20, XFontStyle.Bold);
DrawWatermark(page, font);
XGraphics gfx = XGraphics.FromPdfPage(page);
string total = "Total price: " + ord.Totalprice;
string remark = "Remark: " + ord.Remark;
string status = "Payment status: " + ord.PaymentStatus;
gfx.DrawString(total, font, XBrushes.Black, 10, 100);
gfx.DrawString(remark, font, XBrushes.Black, 10, 135);
gfx.DrawString(status, font, XBrushes.Black, 10, 170);
document.Save(stream, false);
return File(stream.ToArray(), "application/pdf", "Invoice.pdf");
}
another method
void DrawWatermark(PdfPage page, XFont font)
{
string watermark = "CartMart";
// Variation 2: Draw a watermark as an outlined graphical path.
// NYI: Does not work in Core build.
// Get an XGraphics object for drawing beneath the existing content.
var gfx1 = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);
// Get the size (in points) of the text.
var size = gfx1.MeasureString(watermark, font);
// Define a rotation transformation at the center of the page.
gfx1.TranslateTransform(page.Width / 2, page.Height / 2);
gfx1.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx1.TranslateTransform(-page.Width / 2, -page.Height / 2);
// Create a graphical path.
var path = new XGraphicsPath();
// Create a string format.
var format = new XStringFormat();
format.Alignment = XStringAlignment.Near;
format.LineAlignment = XLineAlignment.Near;
// Add the text to the path.
// AddString is not implemented in PDFsharp Core.
path.AddString(watermark, font.FontFamily, XFontStyle.BoldItalic, 150,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
format);
// Create a dimmed red pen.
var pen = new XPen(XColor.FromArgb(128, 255, 0, 0), 2);
// Stroke the outline of the path.
gfx1.DrawPath(pen, path);
gfx1.Dispose();
}
there is no watermark in pdf file. can somebody help me with the code. i want to display data as well and i dont know whats going on. There is no error as well.
Seems you mixed up a bit variations 2 and 3 from here. In variation 2, you should use XGraphicsPdfPageOptions.Prepend to draw beneath instead of above.
Also, if you're using .NET Core, it seems only variation 1 works. Looks like there is a port for Core here but no idea what is the status of this development.
I'm trying the create a multiple-page pdf using iTextSharp, but I'm having some issue creating a loop to have more than a single page.
My code below is working well for one page; however, my content will not fit into a single page. Thanks.
How can I write a loop to write contents on the first pdf page and the remaining to the second, third, etc...So far, I'm only seeing one page. Thank you.
int height = 600;
int totalPage = 1;
int oldPage = 1;
bool cons = false;
bool st = false;
foreach (string al in combo)
foreach (string al in combo) //my loop to write on the pdf but "combo" has 100 lines, which would fit into a single page.
{
string[] word = al.Split(',');
int strIndex = combo.IndexOf(al);
if (al.Length != 0)
{
if (word[0].ToString().ToUpper().Contains("(REV")==true && cons == false)
{
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "CONSTRUCTION PRINTS", 80, height, 0);
cb.EndText();
height = height - 20;
cons = true;
}
if (word[0].ToString().ToUpper().Contains("(REV")==false && st == false)
{
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "SAG & TENSION CHARTS", 80, height, 0);
cb.EndText();
height = height - 20;
st = true;
}
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 11);
totalPage = totalPage + oldPage;
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, word[0].ToString().ToUpper(), 80, height, 0);
cb.EndText();
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "......................................................................................", 335, height, 0);
cb.EndText();
// write the text in the pdf content
cb.BeginText();
// put the alignment and coordinates here
cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT, totalPage.ToString(), 500, height, 0);
cb.EndText();
oldPage = Convert.ToInt32(word[1]);
}
height = height - 20;
}
//// create the new page and add it to the pdf
// reader = new PdfReader(oldFile);//old file
PdfImportedPage page = writer.GetImportedPage(reader, 1);
cb.AddTemplate(page, 0, 0);
//// close the streams and voilá the file should be changed :)
document.Close();
reader.Close();
fs.Close();
writer.Close();
Please go to the official documentation and click Q&A to go to the most frequently asked questions. Pick the Getting started category. The first thing you'll see, is the most popular iText example (which I am porting to C# for your convenience):
// step 1
Document document = new Document();
// step 2
FileStream fs = new FileStream("hello.pdf", FileMode.Create);
PdfWriter.GetInstance(document, fs);
// step 3
document.Open();
// step 4
document.Add(new Paragraph("Hello World!"));
// step 5
document.Close();
In your code, you are adding text at absolute positions by introducing PDF syntax, such as BeginText() (BT) / EndText() (ET) / SetFontAndSize() (Tf). This is creating PDF the hard way. If is very easy to make mistakes, as shown in this question: What is causing syntax errors in a page created with iText?
If you don't know the PDF reference (ISO 32000-1) by heart, you should avoid code like this. Instead you can use objects such as Paragraph, List, PdfPTable... The beauty of adding these objects to a Document using the Add() method, is that a new page gets triggered automagically as soon as a page is full. You can also trigger a new page yourself using:
document.NewPage();
If you want to add text at absolute positions, iText offers convenience methods and objects such as ColumnText. See my answer to Adding footer to existing PDF
You can define a Rectangle and add objects such as Paragraph, List, PdfPTable... Take a look at the answer to the question How to continue an ordered list on a second page? for inspiration:
ColumnText ct = new ColumnText(cb);
ct.AddElement(list);
Rectangle rect = new Rectangle(36, 36, 559, 806);
ct.SetSimpleColumn(rect);
int status = ct.Go();
while (ColumnText.HasMoreText(status)) {
document.NewPage();
ct.SetSimpleColumn(rect);
ct.Go();
}
The while loop is the loop you are looking for.
Goal
I need to increase the quality of the bitmap that is being added to a pdf. I have a 300dpi image, but looks really bad when itextsharp adds it to pdf.
Description
I am working on a project which takes a bitmap and writes it to a pdf using itextSharp. The problem is that the project makes a graph, saves it as a bitmap, writes it to the pdf, and the graph appears to be low quality. I am unable to attach the picture here of what is being graphed, but will do my best to describe what is going on. Here is the code below:
//Declare chart object
private static System.Windows.Forms.DataVisualization.Charting.Chart chart_runs;
//Start the PDF Document
Document pdfDocument = new Document(PageSize.LETTER);
private static System.Drawing.Bitmap GetChartBitmap()
{
chart_runs.Width = 2269;
chart_runs.Height = 1406;
System.Drawing.Bitmap bitmap1 = new System.Drawing.Bitmap(2269, 1406);
bitmap1.SetResolution(300, 300);
chart_runs.DrawToBitmap(bitmap1, new System.Drawing.Rectangle(0, 0, 2269, 1406));
string path = System.IO.Path.GetTempPath();
bitmap1.Save(path + #"\Image.png");
return bitmap1;
}
Here is how the bitmap and image is added to the pdf.
System.Drawing.Bitmap img = GetChartBitmap();
img.Dispose();
string path = System.IO.Path.GetTempPath();
iTextSharp.text.Image imag
= iTextSharp.text.Image.GetInstance(path + #"\Image.bmp");
//imag.ScaleAbsolute(chart_runs.Width, chart_runs.Height);
//imag.ScaleToFit(550, 330);
imag.ScaleAbsolute(550, 350);
//imag.ScaleAbsolute(650, 450);
//imag.ScaleAbsolute(100f, 100f);
pdfDocument.Add(imag);
pdfDocument.Close();
pdfDocument.Dispose();
Attempts
I have spent considerable time trying to fix this, here are some of the highlights.
At first, it was believed this was a DPI issue. However, when I made
the bitmap (and itext image) 300DPI, no quality difference was
noticed.
I then added more pixels by enlarging the targetBounds object above. This had the weird effect of shrinking my graph on my PDF? Why?
I have attempted many manipulations with itext.scale functions as I found here.
Another justification why I am trying to make my bitmap larger is on here.
As I said above, I seem to be making my image even smaller with the current approach. I am just trying to increase the quality of the bitmap so when people print it off or zoom in on the pdf it doesn't look terrible. I appreciate any help.
Not an answer yet
Here's a very simple image generating function just for testing purposes, it should be fairly self-explanatory:
private static byte[] CreateImage(string text, int fontSize, int width, int height) {
using (var b = new Bitmap(width, height)) {
using (var g = Graphics.FromImage(b)) {
using(var br = new SolidBrush(System.Drawing.Color.Red)){
using (var f = new System.Drawing.Font("Arial Unicode MS", fontSize)) {
g.DrawString(text, f, br, 10, 10);
using (var ms = new System.IO.MemoryStream()) {
b.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
return ms.ToArray();
}
}
}
}
}
}
If you examine the PDF generated from the below code that uses the function above you'll see each image in the PDF gets progressively "better" and you can zoom in further before seeing jagged lines. Ultimately you're working with raster/bitmap files, however, so some pixelation will almost always eventually occur. The DPI listed is the "effective DPI" which is based on the assumption that the PDF will map 72 units into 1 inch of space (which is the PDF standard's default) and that it is printed at 100% resolution.
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
using (var fs = new FileStream(testFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, fs)) {
doc.SetMargins(0, 0, 0, 0);
doc.Open();
var img1 = iTextSharp.text.Image.GetInstance(CreateImage("Hello", 24, 600, 50));
doc.Add(img1); //72 DPI
var img2 = iTextSharp.text.Image.GetInstance(CreateImage("Hello", 48, 1200, 100));
img2.ScaleAbsolute(600, 50);
doc.Add(img2); //144 DPI
var img3 = iTextSharp.text.Image.GetInstance(CreateImage("Hello", 96, 2400, 200));
img3.ScaleAbsolute(600, 50);
doc.Add(img3); //288 DPI
var img4 = iTextSharp.text.Image.GetInstance(CreateImage("Hello", 192, 4800, 400));
img4.ScaleAbsolute(600, 50);
doc.Add(img4); //576 DPI
doc.Close();
}
}
}
I am using GhostScript.Net to rasterize PDF to page images before sending the page images to the printer. I am doing this so that I can always rasterize to 300dpi. This allows me to print the PDF in a reasonable amount of time regardless of the size of any image in the PDF (mainly scanned PDFs).
However, it strikes me that in some cases there will not be a need to rasterize as high as 300dpi. It may be possible to rasterize to 200dpi or even 100dpi depending on the content of the page.
Has anyone attempted to determine the maximum DPI for the content of a PDF page? Perhaps using iTextSharp?
My current code is this:
var dpiList = new List<int> {50, 100, 150, 200, 250, 300, 350, 400, 450, 500};
string inputPdfPath = #"C:\10page.pdf";
string outputPath = #"C:\Print\";
var lastInstalledVersion =
GhostscriptVersionInfo.GetLastInstalledVersion(
GhostscriptLicense.GPL | GhostscriptLicense.AFPL,
GhostscriptLicense.GPL);
var rasterizer = new GhostscriptRasterizer();
rasterizer.Open(inputPdfPath, lastInstalledVersion, true);
var imageFiles = new List<string>();
for (int pageNumber = 1; pageNumber <= 10; pageNumber++)
{
foreach (var dpi in dpiList)
{
string pageFilePath = System.IO.Path.Combine(outputPath,
string.Format("{0}-{1}-{2}.png", pageNumber, Guid.NewGuid().ToString("N").Substring(0, 8), dpi));
System.Drawing.Image img = rasterizer.GetPage(dpi, dpi, pageNumber);
img.Save(pageFilePath, ImageFormat.Png);
imageFiles.Add(pageFilePath);
Console.WriteLine(pageFilePath);
}
}
var imageCount = 0;
var pd = new PrintDocument();
pd.PrintPage += delegate(object o, PrintPageEventArgs args)
{
var i = System.Drawing.Image.FromFile(imageFiles[imageCount]);
var pageBounds = args.PageBounds;
var margin = 48;
var imageBounds = new System.Drawing.Rectangle
{
Height = pageBounds.Height - margin,
Width = pageBounds.Width - margin,
Location = new System.Drawing.Point(margin / 2, margin / 2)
};
args.Graphics.DrawImage(i, imageBounds);
imageCount++;
};
foreach (var imagefile in imageFiles)
{
pd.Print();
}
PDF pages don't have a resolution. Images within them can be considered to have a resolution, which is given by the width of the image on the page, divided by the number of image samples in the x direction, and the height of the image on the page divided by the number of image samples in the y direction.
So this leaves calculating the width and height of the image on the page. This is given by the image matrix, modified by the Current Transformation Matrix. So in order to work out the width and height on the page, you need to interpret the content stream up to the point where the image is rendered, tracking the graphics state CTM.
For general PDF files, the only way to know this is to use a PDF interpreter. In the strictly limited case where the whole page content is a single image you can gamble that there is no scaling taking place and simply divide the media width by the image width, and the media height by the image height to give the x and y resolutions.
However this definitely won't work in the general case.
I have an app that's basically a fancy PDF reader. I download a PDF from the internet and generate thumbnails for that PDF. However, it seems that when I generate these thumbnails a lot of memory is being allocated (checked using Instruments), sometimes parts of this is collected by the GC but in the end, my app gives up. I've had memory usage of up to 38Mb when generating thumbnails for a single PDF (100x100 thumbs, ~60 pages).
I generate one thumbnail at a time, store it and then repeat the process, so under any circumstance there should only be one thumbnail in memory (while generating them, at least). My code for generating thumbnails looks like this:
public UIImage GetPageThumbnail(int pageNumber, SizeF size)
{
//If using retina display, make sure to scale-up the thumbnail as well.
size.Width = size.Width * UIScreen.MainScreen.Scale;
size.Height = size.Height * UIScreen.MainScreen.Scale;
UIGraphics.BeginImageContext(size);
CGContext tempContext = UIGraphics.GetCurrentContext();
CGPDFPage page = Document.GetPage(pageNumber);
RectangleF drawArea = new RectangleF(new PointF(0f, 0f), size);
CGAffineTransform transform = page.GetDrawingTransform( CGPDFBox.Crop, drawArea, 180, true); //fit PDF to context
transform.xx = -transform.xx; // }
transform.x0 = 0; // }flip horizontally
//Console.WriteLine("XX: " + transform.xx + ", YX:" + transform.yx + ", XY:" + transform.xy + ", YY:" + transform.yy + ", X0:" + transform.x0 + ", Y0:" + transform.y0);
tempContext.ConcatCTM(transform);
tempContext.DrawPDFPage (page);
UIImage returnImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return returnImage;
}
I've tried explicitly disposing the context and PDF page, but that had no effect (actually it seemed worse, but take that with a pinch of salt).
I've seen some posts about memory leakage with MonoTouch and PDF (basically this post), but that's pretty old. I'm using the newest MonoTouch (5.0.2).
Not sure where the problem in your code is, but here's my code for generating thumbs of PDF pages. It is working flawlessly. Maybe it helps you. I think your issue might be what you are doing with the returned image when you're don.
public static UIImageView GetLowResPagePreview (CGPDFPage oPdfPage, RectangleF oTargetRect)
{
RectangleF oOriginalPdfPageRect = oPdfPage.GetBoxRect (CGPDFBox.Media);
RectangleF oPdfPageRect = PdfViewerHelpers.RotateRectangle( oPdfPage.GetBoxRect (CGPDFBox.Media), oPdfPage.RotationAngle);
// Create a low res image representation of the PDF page to display before the TiledPDFView
// renders its content.
int iWidth = Convert.ToInt32 ( oPdfPageRect.Size.Width );
int iHeight = Convert.ToInt32 ( oPdfPageRect.Size.Height );
CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB();
CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedLast);
// First fill the background with white.
oContext.SetFillColor (1.0f, 1.0f, 1.0f, 1.0f);
oContext.FillRect (oOriginalPdfPageRect);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
oContext.ConcatCTM ( oPdfPage.GetDrawingTransform ( CGPDFBox.Media, oPdfPageRect, 0, true ) );
oContext.DrawPDFPage (oPdfPage);
CGImage oImage = oContext.ToImage();
UIImage oBackgroundImage = UIImage.FromImage( oImage );
oContext.Dispose();
oImage.Dispose ();
oColorSpace.Dispose ();
UIImageView oBackgroundImageView = new UIImageView (oBackgroundImage);
oBackgroundImageView.Frame = new RectangleF (new PointF (0, 0), oPdfPageRect.Size);
oBackgroundImageView.ContentMode = UIViewContentMode.ScaleToFill;
oBackgroundImageView.UserInteractionEnabled = false;
oBackgroundImageView.AutoresizingMask = UIViewAutoresizing.None;
return oBackgroundImageView;
}