Saving a scrollable panel as PDF using .NET - c#

I have a Panel filled with a lot of controls for users to fill. These include textboxes, checkboxes, radiobuttons etc. It is a long form to fill so the controls are in a scrollable panel. What I need is to save the whole panel as pdf. I think PDFsharp is a good library to be able to save any text or image as a pdf file but I don't want to write code for every single control inside the panel. I once wrote a class to create a pdf file from a Control object. It was iterating all inner controls (and their inner controls until no inner control is left) of the given control and write their Text property (yes/no for chekable controls) to pdf using their Location and Size properties. I could not find it now but I remember it was having issues with some of the DevExpress controls I use so I didn't bother writing it again. (Edit: I had to, you can find it below.) I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it. This question seems like it but there is no satisfying answer to that.
So, screenshot or not I'm open for any advice. There should be many occasions where users must fill long forms and be able to keep it as pdf. Again, any advice or workaround would be appreciated. (I think about creating the form using html, displaying it in a WebBrowser control and using an html to pdf library but I really prefer using my existent form)
Many Thanks.
Edit:
I had to write something iterates inner controls of a container control (like a panel) and writes every inner control to a pdf using their Location, Size and Font properties though, I don't recommend to use it (at least as it is) because of these:
It sets the page's size to given control's size and use only one (usually huge) pdf page. You can add a logic to split it to pages if you need to. (I didn't, but I guess you'll probably need your pdf more printer friendly).
Cheeso's method (using a FlowDocument) is a much more "legitimate" way for a task like this. I prefer using that over this but I didn't have a choice in this instance.
I used PDFsharp in this code. You can find it in it's hompage or it's CodePlex page.
PdfReport class:
private PdfDocument Document;
public Control Control { get; private set; }
public PdfReport(Control control) { Control = control; }
public PdfDocument CreatePdf(PdfDocument document = null)
{
Document = document != null ? document : new PdfDocument();
PdfPage page = Document.AddPage();
page.Height = Control.Height;
page.Width = Control.Width;
XGraphics gfx = XGraphics.FromPdfPage(page);
foreach (PdfItem item in CreatePdf(new Point(0, 0), Control.Controls))
{
XStringFormat format = item.IsContainer ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.BottomCenter ? XStringFormats.BottomCenter : item.TextAlign == ContentAlignment.TopLeft ? XStringFormats.TopLeft : item.TextAlign == ContentAlignment.TopCenter ? XStringFormats.TopCenter : XStringFormats.Center;
gfx.DrawString(item.Text, item.Font, item.Brush, new XRect(item.Location, item.Size), format);
}
return Document;
}
private IEnumerable<PdfItem> CreatePdf(Point location, Control.ControlCollection controls)
{
List<PdfItem> items = new List<PdfItem>();
foreach (Control control in controls)
{
if (control.Controls.Count > 0)
items.AddRange(CreatePdf(control.Location, control.Controls));
items.Add(new PdfItem(control, location));
}
return items;
}
public void SaveAsPdf(string path, bool open = false)
{
CreatePdf().Save(path);
if (open)
Process.Start(path);
}
PdfItem class:
public string Text { get; set; }
public Point Location { get; set; }
public Size Size { get; set; }
public Font Font { get; set; }
public bool IsContainer { get; set; }
public ContentAlignment TextAlign { get; set; }
public Color ForeColor { get; set; }
public XBrush Brush { get { return new SolidBrush(ForeColor); } }
public PdfItem() { }
public PdfItem(string text, Point location, Font font, Color foreColor, Size size, bool isContainer = false, ContentAlignment alignment = ContentAlignment.MiddleCenter)
{
Text = text;
Location = location;
Size = size;
Font = new Font(font.FontFamily, font.Size, font.Style, GraphicsUnit.World);
TextAlign = alignment;
ForeColor = foreColor;
IsContainer = isContainer;
}
public PdfItem(string text, Point location, Size size)
: this(text, location, new Font("Calibri", 12), Color.Black, size) { }
public PdfItem(Control control, Point parentLocation)
: this(control.Text, control.Location, control.Font, control.ForeColor, control.Size, control.Controls.Count > 0)
{
Location = new Point(Location.X + parentLocation.X, Location.Y + parentLocation.Y);
IEnumerable<PropertyInfo> properties = control.GetType().GetProperties();
if (properties.FirstOrDefault(p => p.Name == "TextAlign" && p.PropertyType == typeof(ContentAlignment)) != null)
TextAlign = (control as dynamic).TextAlign;
if (properties.FirstOrDefault(p => p.Name == "Checked" && p.PropertyType == typeof(bool)) != null)
{
string title = control.Text != null && control.Text.Length > 0 ? string.Format("{0}: ", control.Text) : string.Empty;
Text = string.Format("{0}{1}", title, (control as dynamic).Checked ? "Yes" : "No");
}
}

Regarding
. I think taking a screenshot and save that image as pdf would be nice but I couldn't find out how to achieve it.
There is a tool called "cropper" available on codeplex.com. It is designed to be used as a user tool that can take screenshots. It is managed code, open source.
I can imagine embedding some of the cropper magic into your app so that you could take that screenshot. I can also imagine this would be useful for collecting a diagnostic image of the screen at the time of a problem.
On the other hand... if you are interested in producing a printed form that reproduces the content on the screen, then I think you should be using WPF, in which case doing what you want is pretty easy. For example, this question describes how to do a print-preview for a FlowDocument. From that point your user can print to PDF (if he has a PDF printer installed) or print to XPS, or print to a physical output device, and so on.

I don't know if this would help you or not, but DocRaptor.com's pdf api could be built in so it would do it for you, no matter what the user inputs. It uses basic html.

As you can use the below :)
YourPanel.AutoSize = true;
int width = YourPanel.Size.Width;
int height = YourPanel.Size.Height;
Bitmap bm = new Bitmap(width, height);
YourPanel.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
string outputFileName = #"C:\YourDirectory/myimage.bmp";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
bm.Save(memory, ImageFormat.Bmp);
Clipboard.SetImage(bm);
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
YourPanel.AutoSize = false;
The Clipboard.SetImage will send you bm to the clipboard so you can paste them to your pdf form or whatever document
This also has an example built in that saves it as a image for you if you want.
The trick here is Autosize for your panel. It needs to be set to true so the panel resizes itself as a whole area visible, then right after you do your work you can resize it to false so it uses scrollbars again for the users screen (you may see it flash for half a second, but this code does work.
Saving it in a PDF I personally just prefer to write it there as my clipboard or you can write byte. But ITextSharp is a great library for the extension to work with!
I Really hope this helps.

Related

How to check the image property of an pictureBox within a form in another form.

I have create a image box which changes the image within it when it is click. It is stored in form A. I now have a form b. I would like to check in form b. if the image in the picture box in form A is equal to a specific image then change the image in a picturebox in form B.
I am have problems accessing the pic box in form A due to I think it being private.
Despite the apparent simple nature of the problem, the correct way to accomplish this is deceptively complex. You may want to rethink the architecture of your application to perform this check another way, such as with an associated ID number or UUID. However, if you do in fact need to perform a content equality check on images in this manner, as follows is a basic solution that I have tested.
Since it would be painfully inefficient for us to directly compare two images to determine their equality, the best solution is to implement a simple hashing algorithm and check that instead. This is the most complicated part of the solution. To perform this automatically and abstract this step away, we can create a simple wrapper class around the standard Image object as follows:
public class ComparableImage
{
public Image Image
{
get { return this.image; }
set { SetImage(value); }
}
public string SHA1
{
get { return this.sha1; }
}
private Image image = null;
private string sha1 = string.Empty;
// This is the important part that lets us
// efficiently compare two images:
public override bool Equals(object image)
{
if ((image != null) && (image is ComparableImage) )
{
return (sha1.Equals(((ComparableImage)image).sha1));
}
else return false;
}
public override int GetHashCode()
{
return sha1.GetHashCode();
}
private void SetImage(Image image)
{
this.image = image;
this.sha1 = ComputeSHA1(image);
}
private static string ComputeSHA1(Image image)
{
if (image != null)
{
var bytes = GetBytes(image);
using (SHA1Managed SHA1 = new SHA1Managed())
{
return Convert.ToBase64String(SHA1.ComputeHash(bytes));
}
}
else return string.Empty;
}
private static byte[] GetBytes(Image image)
{
using (var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Bmp);
return stream.ToArray();
}
}
}
Using the above class, we can simply call the Equals method to find out if two images are equal in content, without ever having to worry about anything further other than creating ComparableImage objects.
The next issue is exposing a public Image property from each of the forms which enables us to access an instance of ComparableImage. We can easily accomplish this using the following pattern:
public class ComparableImageForm : Form
{
// This is the property we need to expose:
public ComparableImage Image
{
get { return this.image; }
set { SetImage(value); }
}
private ComparableImage image;
private PictureBox pictureBox = new PictureBox()
{
Dock = DockStyle.Fill
};
public ComparableImageForm()
{
this.Controls.Add(pictureBox);
}
// For clarity, we are also setting a picture box image
// from the ComparableImage when it is assigned:
private void SetImage(ComparableImage image)
{
this.image = image;
pictureBox.Image = image.Image;
}
}
Finally, we are ready to load some images do some test comparisons:
// Load two images from file to compare.
// In practice, images can be loaded from anywhere,
// even from the designer.
var image1 = new ComparableImage()
{
Image = Image.FromFile("bitmap1.bmp")
};
var image2 = new ComparableImage()
{
Image = Image.FromFile("bitmap2.bmp")
};
// Create two forms that have picture boxes:
var formA = new ComparableImageForm()
{
Image = image1
};
var formB = new ComparableImageForm()
{
Image = image2
};
// Perform the check to see if they are equal:
if (formA.Image.Equals(formB.Image))
{
MessageBox.Show("The images are indeed equal.");
}
else
{
MessageBox.Show("The images are NOT equal.");
}
// Since images are compared based on their SHA1 hash,
// it does not matter where the image comes from as long
// as the data is the same. Here, we are loading another
// copy of 'bitmap1.bmp':
var anotherImage = new ComparableImage()
{
Image = Image.FromFile("bitmap1.bmp")
};
// The following statement will evaluate as true:
bool isEqual = (anotherImage.Equals(image1));
Thats it!
You should make an instance from your form B. search in controls of that form, find picturebox and change its properties. and show that instance everytime you want to show that form.
It's not a good idea to compare images to detect if they are the same.
It would be better to give every picture an unique ID and store that in a variable. Make sure both forms haven access to that variable, so you can compare it with the ID of the image in form2.

Detecting which image is used in an imagebox

I'm trying too make a memory game.
In this game when a button is klicked the button and an picturebox will be send into a List.
I want too use the images inside the pictureboxes as a way too use this code. But even when the two images are the same the code wont work. Is there a way too check the image used like Name.jpg.
if(buttonCount == 2)
{
if(pictureList[0].Image == pictureList[1].Image)
{
buttonCount = 0;
buttonList.RemoveAt(0)
buttonList.RemoveAt(0);
pictureList.RemoveAt(0);
pictureList.RemoveAt(0);
}
}
You could save an Id of the image (or e.g. the filename like you suggested) in the Tag.
So when loading the image into the picture box:
string path = "PATH";
pictureBox.Image = Image.FromFile(path);
pictureBox.Tag = path;
Then you could compare the Tag.
BUT I think (show us how you load the images please) this is not working as it is, because you load the image twice from the disk like:
pictureBox1.Image = Image.FromFile(path);
pictureBox2.Image = Image.FromFile(path);
Because then you have differen instances and so the equals returns false.
If you do it like the following it should also work:
var image = Image.FromFile(path);
pictureBox1.Image = image;
pictureBox2.Image = image;
In your current application, you do not have enough information associated with the image object to identify it. As such, you need to possibly extend the Image class to include this information or store it in some other way for comparison.
Extending Image class
public class GameImage : Image
{
public static GameImage GetImage(string filename)
{
GameImage img = (GameImage)Image.FromFile(filename);
img.FileName = filename;
return img;
}
public string FileName { get; private set; }
}
Then the comparison becomes
if(buttonCount == 2)
{
if(((GameImage)pictureList[0].Image).FileName == ((GameImage)pictureList[1].Image).FileName)
{
buttonCount = 0;
buttonList.RemoveAt(0)
buttonList.RemoveAt(0);
pictureList.RemoveAt(0);
pictureList.RemoveAt(0);
}
}
Note: Note tested!

PrintDocument using multiple page sizes

Working in .NET 3.5.
Summary:
Trying to replicate functionality of an existing third party component, which breaks in Windows 7.
Until now the user could select a bunch of image files to print, specify a page size for each image and then send them off to print all in one go. I am in dire need of a conceptual explanation of how to go about printing switching the page size on the fly when printing each page.
Details
So far I have figured out how to print multiple images all with the same page size. I use a list of images and use a PrintDocument object, setting the HasMorePages property of the PrintPageEventArgs to true until I reach the end of the list.
Here's a class I quickly threw together to test this:
public partial class Form1 : Form
{
private List<Image> images { get; set; }
private PrintDocument printDocument { get; set; }
public Form1()
{
InitializeComponent();
this.images = new List<Image>();
this.images.Add(Image.FromFile(#"C:\test60.bmp"));
this.images.Add(Image.FromFile(#"C:\SuperLargeTest.jpg"));
this.printDocument = new PrintDocument()
{
PrinterSettings = new PrinterSettings()
};
this.printDocument.PrintPage += printDocument_PrintPage;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
e.PageSettings.PaperSize = this.paperSizes[this.currentImageIndex];
RectangleF marginBounds = e.MarginBounds;
RectangleF printableArea = e.PageSettings.PrintableArea;
int availableWidth = (int)Math.Floor(printDocument.OriginAtMargins ? marginBounds.Width : (e.PageSettings.Landscape ? printableArea.Height : printableArea.Width));
int availableHeight = (int)Math.Floor(printDocument.OriginAtMargins ? marginBounds.Height : (e.PageSettings.Landscape ? printableArea.Width : printableArea.Height));
g.DrawRectangle(Pens.Red, 0, 0, availableWidth - 1, availableHeight - 1);
g.DrawImage(this.images[currentImageIndex], printableArea);
e.HasMorePages = ++currentImageIndex < this.images.Count();
}
private void button1_Click(object sender, EventArgs e)
{
this.printDocument.OriginAtMargins = false;
this.printDocument.Print();
}
}
The thing that I really can't figure out is how to go about changing the page size for, say, the second image.
If I wanted the first image to print in A4 and then the second one to print on A3, how would I go about doing that?
I found this SO question here which seemed to suggest changing the PageSize in the PrintPageEventArgs, but had no joy there.
I also tried to use the QueryPageSettingsEventArgs event and set the PageSettings there, but that didn't seem to work either...
What I would like to achieve is print multiple pages of different size as a single document. Any suggestions, links, explanations, sample code would be very much appreciated.
Anything in C# or VB.NET is fine.
That's work for me too.
Translated to C#:
private bool SetPaperSize(PrintDocument pd, PaperKind nKind)
{
foreach(System.Drawing.Printing.PaperSize ps in pd.PrinterSettings.PaperSizes)
{
if (ps.Kind == nKind)
{
pd.DefaultPageSettings.PaperSize = ps;
return true;
}
}
return false;
}
In VB.NET .. You can use this Sub ..
DocPrint is PrintDocument var
Sub SetPaperSize(ByVal nKind As PaperKind)
Dim ps As PaperSize
For ix As Integer = 0 To DocPrint.PrinterSettings.PaperSizes.Count - 1
If DocPrint.PrinterSettings.PaperSizes(ix).Kind = nKind Then
ps = DocPrint.PrinterSettings.PaperSizes(ix)
DocPrint.DefaultPageSettings.PaperSize = ps
End If
Next
End Sub
Hope this help ..
If you want all the pages to appear as one job (in short avoid being interleaved with other jobs), you can set the page size for the next page inside the PrintPage event handler by changing the default page size of the PrintDocument object.

Printing on a paper form with preview

We've got a large stock of paper forms that we need to fill out. It's very tedious to do this by hand, so we're building an application. It should provide a form to fill in data, be able to show print preview, print the data on the paper form, and keep the history.
Currently, we have a FixedPage which we print like this:
var dlg = new PrintDialog();
if (dlg.ShowDialog() == true)
{
var doc = new FixedDocument();
doc.DocumentPaginator.PageSize = new Size(11.69 * 96, 8.27 * 96); // A4 Landscape
var fp = Application.LoadComponent(new Uri("/FixedPage.xaml", UriKind.Relative)) as FixedPage;
fp.DataContext = this;
fp.UpdateLayout();
var pc = new PageContent();
((IAddChild)pc).AddChild(fp);
doc.Pages.Add(pc);
dlg.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
dlg.PrintDocument(doc.DocumentPaginator, string.Format("Form #{0}", FormNumber));
}
For the print preview we have a custom UserControl with scanned image of the paper form on background and the data on foreground. Basically, it's repeating the FixedPage layout, and all this makes us think there's a flaw in our design.
Is there a better way to do what we want?
I was tasked with the same problem and wanted to avoid writing my own templating system to save time, unit testing, and my sanity.
I ended up writing a hybrid to did some cool things. First, I wrote my templates using HTML and CSS. It was very easy to do and allowed for great flexibility when making minor adjustments from our Marketing department.
I filled the template with my own tags (e.g [code_type/], [day_list]...[/day_list]) and string replaced the text with a dictionary of tags that could be a single or multivalued.
After generating the html, I would use an html to pdf library I found that uses the open-source webkit engine to render and create the generated pdf. It turned out very stable and took around 2 weeks to write the initial program. Everyone was very pleased and testing was a breeze.
If you want more details, send me a message or reply to this.
We have managed to find a solution, which allows us to throw away a bunch of renundant code. It is still ugly:
public class CustomDocumentViewer : DocumentViewer
{
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(Image), typeof(CustomDocumentViewer), new UIPropertyMetadata(null));
public Image BackgroundImage
{
get { return GetValue(BackgroundImageProperty) as Image; }
set { SetValue(BackgroundImageProperty, value); }
}
protected override void OnDocumentChanged()
{
(Document as FixedDocument).Pages[0].Child.Children.Insert(0, BackgroundImage);
base.OnDocumentChanged();
}
protected override void OnPrintCommand()
{
var printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
(Document as FixedDocument).Pages[0].Child.Children.RemoveAt(0);
printDialog.PrintDocument(Document.DocumentPaginator, "Test page");
(Document as FixedDocument).Pages[0].Child.Children.Insert(0, BackgroundImage);
}
}
}
...
<local:CustomDocumentViewer x:Name="viewer" BackgroundImage="{StaticResource PaperFormImage}"/>
...
InitializeComponent();
viewer.Document = Application.LoadComponent(new Uri("/PaperFormDocument.xaml", UriKind.Relative)) as IDocumentPaginatorSource;
The reason why we're using Application.LoadComponent instead of binding is a five years old bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=293646

How to identify end of page is reached in pdf file using itextsharp

Hi
I am using itextsharp to generate a pdf file.I am placing a backgound image on it and want that image on all the pages .But when the first page is completed the text move to next page automatically so the image is not appearing on the new page.
Is there a way to identify the end of page so that we can add a new page and then set the image first so will appear in background and then can add the remaining text.
All is i want a image in background on all the pages of pdf file.
I suggest you use a page event:
myWriter.setPageEvent(new BackgroundPageEvent(backgroundImage));
class BackgroundPageEvent extends PdfPageEventHelper {
Image backgroundImage = null;
public BackgroundPageEvent( Image img ) {
backgroundImage = img;
}
public void onStartPage(PdfWriter writer, Document doc) {
PdfContentByte underContent = writer.getDirectContentUnder();
underContent.addImage(backgroundImage);
}
}
With the above code, backgroundImage will be added to the "under content" as each page is created. No need to worry about when to add it yourself... iText will figure that out for you, and the first thing in the underContent of each page will be your image. You might need to play around with the various overrides of addImage to get the size you want.
I believe you can also query doc for the current page size if it varies in your program. If not, you should be able to create the image you pass in with an absolute position/scale (which may be what you're doing already).
PdfPageEvent has a number of other events you can override. PdfPageEventHelper covers all the bases with "no ops" so you can just override the event[s] you want:
OnStartPage
OnEndPage
OnCloseDocument
OnParagraph
OnParagraphEnd
OnChapter
OnChapterEnd
OnSection
OnSectionEnd
OnGenericTag
Generic tag is actually Really Handy. You can give a generic tag (a string) to just about anything within your document, and your OnGenericTag override will be called with the rect that was used to draw whatever it was you tagged. All kinds of spiffy possibilities.
Just check PdfWriter.PageNumber property like this:
using (FileStream fs = File.Create("test.pdf"))
{
Document document = new Document(PageSize.A4, 72, 72, 72, 72);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
int pageNumber = -1;
for (int i = 0; i < 20; i++)
{
if (pageNumber != writer.PageNumber)
{
// Add image
pageNumber = writer.PageNumber;
}
// Add something else
}
document.Close();
}

Categories

Resources