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.
Related
When i run the program, i get a different amount of images(from 20 all the way up to 2000) and i would like to merge all of these images into one image which would preferably be a square.
This is the code i have for getting the file images(the images are in URL format)
int ximg = 1;
int totalImgs = richTextBox1.Lines.Count();
while (ximg < totalImgs)
{
System.Net.WebRequest request = System.Net.WebRequest.Create(richTextBox1.Lines[ximg]);
System.Net.WebResponse response = request.GetResponse();
System.IO.Stream responseStream =
response.GetResponseStream();
Bitmap image = new Bitmap(responseStream);
List<Image> fileList = new List<Image>();
fileList.Add(image);
ximg++;
}
Also every single image has a title in a different richtextbox which i would like to know if it is possible to add a title under the image (richtextbox1.lines[1] (image) = richtextbox2.lines[1] (title)). Is it possible to add a picture as a background picture when merging(to the square image I want to generate)? Is it possible to add a border on every single image picture and merge them with the border? How can the code calculate when its time to change line and start adding images in the next row?
I've tried this code, but it works only if you know the amount of images you want to merge.
Bitmap bitmap = new Bitmap(image1.Width + image2.Width, Math.Max(image1.Height, image2.Height));
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(image1, 0, 0);
g.DrawImage(image2, image1.Width, 0);
}
bitmap.Save("merged.bmp");
I would do it with photoshop but when there are 2000 images to merge together, I just do not have the time.
Is there any way to accomplish such task? Any references would be appreciated!
I have mentioned it above :) !
This solution will create a mosaic of the images from left to right, top to bottom. It does not do anything to maximize the available space. It also assumes that you have a max width and height for the finished image, since it's not realistic to support an arbitrary size.
// where we store the finished mosaic
var mosaic = new Bitmap(maxWidth, maxHeight);
// track the location where we are drawing each image
var imageCorner = new Point(0,0);
// track the height of the current row
var currentRowHeight = 0;
// track the width and height of the mosaic as it grows.
var mosaicWidth = 0;
var mosaicHeight = 0;
using (var g = Graphics.FromImage(bitmap))
{
var borderPen = new Pen(Brushes.Black) { Width = 2 };
var labelFont = new Font("Arial", 10);
var labelBrush = new SolidBrush(Color.Black);
foreach (var image in imageList)
{
if (imageCorner.X + image.Width > maxWidth)
{
// if adding the image to the current row would make it too wide,
// move to the next row by resetting X to zero and adding the
// height of the tallest image to Y
imageCorner.X = 0;
imageCorner.Y += currentRowHeight;
// since this is a new row, it's current height is zero
currentRowHeight = 0;
}
// if adding this image would put us past the
// height of the image, then we're out of room.
if (imageCorner.Y + image.Height > maxHeight)
{
// this skips images if there's no room for them in
// the mosaic, you may want to do something different
Trace.WriteLine($"Image is {image.Height} pixels tall, but only {maxHeight - mosaicHeight} pixels available.");
continue;
}
// draw the image
g.DrawImage(image, imageCorner);
// draw the border
g.DrawRectangle(borderPen,
imageCorner.X, imageCorner.Y,
image.Width, image.Height)
// draw the label
g.DrawText("Image Label", labelBrush, imageCorner.X, imageCorner.Y)
// now that we've drawn the image, we need to shift to the right
imageCorner.X += image.Width;
// row height is the height of the tallest image so far in this row
currentRowHeight = Math.Max(image.Height, currentRowHeight);
// track the total height of the mosaic
mosaicHeight = imageCorner.Y + currentRowHeight;
// mosaic width is just the widest row in the mosaic
mosaicWidth = Math.Max(imageCorner.X, widthOfWidestRow);
}
}
// trim off the parts of the mosaic we didn't fill
mosaic = mosaic.Clone(new Rectangle(0, 0, mosaicWidth, mosaicHeight);
mosaic.Save("merged.bmp");
If you wanted to minimize wasted space, you could sort your list of images in different ways, or calculate ahead of time what a good width and height for the mosaic would be.
I am making an application that creates a watermark on a PDF that the user selects and I can't seem to get the watermark to appear on the selected PDF but I also get no errors. Any help would be appreciated.
I am using PDFsharp version 1.50.4000
public void WaterMarkPDF(string sourceFileName)
{
try
{
string watermark = "watermark";
int emSize = 100;
string file ="test.pdf";
File.Copy(sourceFileName, file, true);
File.SetAttributes(file, File.GetAttributes(file) & ~FileAttributes.ReadOnly);
// Take in pdf from the form
PdfDocument document = PdfReader.Open(file);
// change the version cause sometimes newer versions break it
if (document.Version < 14)
document.Version = 14;
XFont font = new XFont("Times New Roman", emSize, XFontStyle.BoldItalic);
for (int idx = 0; idx < document.Pages.Count; idx++)
{
var page = document.Pages[idx];
// Get an XGraphics object for drawing beneath the existing content.
var gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Prepend);
// Get the size (in points) of the text.
var size = gfx.MeasureString(watermark, font);
// Define a rotation transformation at the center of the page.
gfx.TranslateTransform(page.Width / 2, page.Height / 2);
gfx.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx.TranslateTransform(-page.Width / 2, -page.Height / 2);
// Create a string format.
var format = new XStringFormat();
format.Alignment = XStringAlignment.Near;
format.LineAlignment = XLineAlignment.Near;
// Create a dimmed red brush.
XBrush brush = new XSolidBrush(XColor.FromArgb(128, 255, 0, 0));
// Draw the string.
gfx.DrawString(watermark, font, brush,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
format);
// Save the document...
document.Save(file);
// ...and start a viewer.
Process.Start(file);
}
}
catch (Exception e)
{
throw e;
}
}
Maybe try XGraphicsPdfPageOptions.Appendinstead of XGraphicsPdfPageOptions.Prepend.
Call document.Save and Process.Startoutside the for loop.
Update: Explanation: With XGraphicsPdfPageOptions.Prepend the watermark is drawn below the original PDF page. Most PDF files consist of black text on transparent background and the watermark will be visible there (you can check this by activating the Transparency Grid in Adobe Reader). For PDF pages with a solid background (e.g. images, tables with a background colour, ...) the watermark will not be visible.
The PDFsharp source code includes a Watermark sample:
http://pdfsharp.net/wiki/Watermark-sample.ashx
There are two variants that add a semi-transparent text on top of the existing PDF page. These variants also work for PDF pages without transparency.
I am using wkHTMLtoPDF and I want to change orientation on the PDF based on the length of the headers in the HTML, but I'm not sure if it's the right way to do this.
private const double A4Width = 2480; // A4 pixel width
Landscape detection method
private bool IsLandscape(string html)
{
int start = html.IndexOf("<th>");
int end = html.LastIndexOf("</th>") - start;
string tableHeadings = html.Substring(start, end).Replace("<th>", string.Empty).Replace("</th>", string.Empty);
FontFamily fontFamily = new FontFamily("Arial");
Font font = new Font(fontFamily, 13);
var size = MeasureString(tableHeadings, font);
if(size.Width > A4Width)
{
return true;
}
return false;
}
Method for font calculus
private SizeF MeasureString(string content, Font font)
{
SizeF result = SizeF.Empty;
using (var image = new Bitmap(1,1))
{
using (var g = Graphics.FromImage(image))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
result = g.MeasureString(content, font, int.MaxValue, StringFormat.GenericTypographic);
}
}
return result;
}
Note: This code is in a library so that is the reason I have used this question to measure string.
Update
To be short:
I am looking for a best practice of implementing the problem.
The problem summary:
I am building from HTML a PDF that has a table inside.
I get the table columns (eg: Col1, Col2) and remove the tags (eg: Col1Col2).
Next I want to calculate the size (in px) of that string result (using a Font with a specified text size), and if that size excedes the A4 portrait size than rotate the PDF to the landscape orientation.
I have found that the best implementation of this problem is to add to wkHTMLtoPDF an attribute to force the PDF to be A4 size.
Attribute used : --page-size A4
Also I have changed the code that checks if the measured width size is grater than the A4 size.
if(size.Width > (A4Width / 4))
{
return true;
}
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();
I have been pulling my hair out on this one for ages now, so thought I'd try to get some help...
I'm working with PDFsharp (may or may not be significant) and trying to put a watermark into a pdf document when it gets downloaded via my asp.net web app.
My problem comes with that the user needs to be able to define the text in the watermark, so I can't use a fixed text size, also, the pdf page size can change.
Assuming that I'm using Arial and bold, and can find the page width in cm/mm/inch/pt, how do I calculate the font's emSize that is needed so that whatever text is entered grows/shrinks to fill the width?
The PDFsharp XFont constructor takes font name and emSize.
Edit:
Many thanks for the suggestions guys, this is what I implemented in the end:
PdfDocument doc = PdfReader.Open(stream, PdfDocumentOpenMode.Modify);
foreach (PdfPage page in doc.Pages)
{
double watermarkWidth = (Math.Sqrt(Math.Pow(page.Height.Point, 2) + Math.Pow(page.Width.Point, 2)));
//reduce by 10% so that the wording doesn't go right into the corners
watermarkWidth -= (watermarkWidth / 10);
XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Prepend);
double emSize = 150;
XFont font = new XFont("Arial", emSize, XFontStyle.Bold);
XSize size = gfx.MeasureString(watermark, font);
while (size.Width > watermarkWidth && emSize > 10)
{
emSize -= 5;
font = new XFont("Arial", emSize, XFontStyle.Bold);
size = gfx.MeasureString(watermark, font);
}
gfx.TranslateTransform(page.Width / 2, page.Height / 2);
gfx.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx.TranslateTransform(-page.Width / 2, -page.Height / 2);
XGraphicsPath path = new XGraphicsPath();
path.AddString(watermark, font.FontFamily, XFontStyle.Bold, emSize,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
XStringFormats.Default);
XPen pen = new XPen(XColor.FromArgb(75, 255, 0, 0), 2);
gfx.DrawPath(pen, path);
}
doc.Save(stream, false);
Is the length of the watermark (in characters) limited? If so, you can take the allowed number of characters and calculate the width of strings like "###...", "WWW..." and "MMM..." in a loop with decreasing font sizes until it fits.
Then you will have a single font size that can be used for all texts.
The watermark will then be smaller - if you calculate the width for 30 "#" and the user just enters "Top Secret" ...
Better method: let the user enter the watermark, then use a loop with decreasing font sizes until the desired text fits into the available space.
Maybe this way (i dont know how PDFsharp works exactly, but in a normal Forms or Wpf this code would deliver the biggest possible Font)
public Font GetFont(Panel Wathermark, string textToPaint)
{
Font result = new Font("Bold", 1);
using (Graphics measure = Wathermark.CreateGraphics())
{
SizeF tempSize;
tempSize = measure.MeasureString(textToPaint, result);
while (tempSize.Width < Wathermark.Width && tempSize.Height < Wathermark.Height)
{
tempSize = measure.MeasureString(textToPrint, result);
var tempSizeToIncrease = result.Size; //writeprotected
result = new Font(result.Name, tempSizeToIncrease += 0.1f);
}
}
return result;
}