Convert html to image with pagination using C# - c#

I'm working on a windows service in c# 4.0 wich transform different file in image (tif and jpeg)
I have a problem when i want to convert a html file (usually an e-mail) in image.
I use WebBrowser
var browser = new WebBrowser();
browser.DocumentCompleted += this.BrowserDocumentCompleted;
browser.DocumentText = html;
and DrawToBitmap
var browser = sender as WebBrowser;
Rectangle body = new Rectangle(browser.Document.Body.ScrollRectangle.X * scaleFactor,
browser.Document.Body.ScrollRectangle.Y * scaleFactor,
browser.Document.Body.ScrollRectangle.Width * scaleFactor,
browser.Document.Body.ScrollRectangle.Height * scaleFactor);
browser.Height = body.Height;
Bitmap output = new Bitmap(body.Width, body.Height);
browser.DrawToBitmap(output, body);
It works fine for small or medium html, but with long html (like 22 000 height px or more)
I have GDI exeptions on DrawToBitmap :
Invalid parameter
Not an image GDI+ valid
According to internet, this kind of error append because the image is too big.
My question : How can i convert html in X images (pagination) without generate the big image and crop after, and if it's possible without using library.
Thank you in advance.
Edit : I found a tricky solution : surround the html with a div witch gonna set the page and another for the offset, for exemple :
<div style="height:3000px; overflow:hidden">
<div style="margin-top:-3000px">
But this solution can crop on a line of text or in the middle of an image...

You can try creating a custom IE Print Template and use DEVICERECT and LAYOUTRECT elements to drive the pagination. The lines wouldn't get cut in the middle then, and you'd capture a bitmap of each DEVICERECT as a page. You'd need to issue CGID_MSHTML/IDM_SETPRINTTEMPLATE command to MSHTML document object (webBrowser.Document.DomDocument as IOleCommandTarget) to enable the Print Template-specific element tags like those. More information about Print Templates can be found here.
[EDITED] You can even use IHTMLElementRender::DrawToDC API on a DEVICERECT object to draw its content on a bitmap DC. You'd need to enable FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI and disable FEATURE_GPU_RENDERING feature control settings for your WebBrowser hosting app to use IHTMLElementRender::DrawToDC.

Thank you for your anwser Noseratio.
I founded a solution by using printing and a virtual printer to get image file.
Save the html in a file and remove all encoding :
html = Regex.Replace(html, "<meta[^>]*http-equiv=\"Content-Type\"[^>]*>", string.Empty, RegexOptions.Multiline);
using (var f = File.Create(filePath))
{
var bytes = Encoding.Default.GetBytes(html);
f.Write(bytes, 0, bytes.Length);
}
Run the print without show the webbrowser and printing popup :
const short PRINT_WAITFORCOMPLETION = 2;
const int OLECMDID_PRINT = 6;
const int OLECMDEXECOPT_DONTPROMPTUSER = 2;
dynamic ie = browser.ActiveXInstance;
ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, PRINT_WAITFORCOMPLETION);
I use PDFCreator for virtual printing and it keep me all files in a folder. It's not easy to get all of this file (know when printing is finish, how many files and when you can use them...) but it isn't the purpose of this post!

Related

How to print svg string without file creation

I generated svg strings in my program c#. How can I print svg strings without file creation?
A simple example using a WebBrowser class.
You can of course use a WebBorwser Control, if you also need to show the SVG on screen.
You can do the same with a WebView2 Control / CoreWebView2 object.
Using a bare-bone HTML5 document text, set the content of an IMG element to the SVG string converted to base64, setting the mime-type of the data to image/svg+xml.
The Width of the IMG element is set to 200 (~2 inches, ~50 millimeters).
Modify as required (considering the DPI, if necessary).
Then create a WebBrowser class, generate a new empty Document object and Write() to it the HTML content (using this method to have immediate rendering of the content).
After that, call the WebBrowser.Print() method to send the rendered SVG to the default Printer.
Assume svgContent is your SVG string:
string base64 = Convert.ToBase64String(svgContent);
int svgWidth = 200;
string html = $"<!DOCTYPE html><html><body>" +
$"<img width={svgWidth} src=\"data:image/svg+xml;base64,{base64}\"/>" +
"</body></html>";
var wb = new WebBrowser() {ScriptErrorsSuppressed = true };
try {
wb.Navigate("");
var doc = wb.Document.OpenNew(true);
doc.Write(html);
wb.Print();
}
finally {
wb.Dispose();
}
You can place <svg>whatever</svg> tags directly inline into your html5 document as text. When a browser receives that document it renders the svg in whatever. This is a double-cool way to do it because your CSS applies to your svg elements.
Here's a decent introduction. Look for the section on how to use inline svg.

Converting aspx page to image file

I have c# dynamic aspx page after new property add I create for record brochure
http://veneristurkey.com/admin/Brochure.aspx?Admin=PropertiesBrochureA4&id=36
but I want to this convert image file I am searching on internet but all with webbrowser and windows forms. I need on page load show not css type also image file. jpg, png or tiff how i can do this. i need to see sample code..
saving aspx page into an image 2
As I mentioned in my comment, your best bet is to opt for attempting to render HTML to an image.
Here is the link for a library that will allow your to render html to an image:
http://htmlrenderer.codeplex.com/
Here is code that does exactly what you're asking:
http://amoghnatu.wordpress.com/2013/05/13/converting-html-text-to-image-using-c/
Now all you have left is to get the html, since I'm assuming you don't want this to render to the browser prior to generating this image - you should look into grabbing the rendered html from the aspx page on the server prior to returning it, and then just return the image. To render a page:
https://stackoverflow.com/a/647866/1017882
Sorted.
If you do not mind using a commandline tool you can have a look at wkhtmltopdf. The package include a wkhtmltoimage component that can be used to convert HTML to image, using
wkhtmltoimage [URL] [Image Path]
Codaxy also wrote a wkhtmltopdf c# wrapper available through the NuGet package manager. I'm not sure if the wkhtmltoimage component was included, but it should be easy enough to figure out how they wrap the wkhtml components.
i fixed my problem with screenshot machine API they are my code..
public void resimyap()
{
var procad = WS.Satiliklars.Where(v => v.ForSaleID == int.Parse(Request.QueryString["id"])).FirstOrDefault();
var imageBytes = GetBytesFromUrl("http://api.screenshotmachine.com/?key=xxxxxx&size=F&url=http://xxxxxxx.com/a4.aspx?id=" + procad.ForSaleID);
string root = Server.MapPath("~/");
// clean up the path
if (!root.EndsWith(#"\"))
root += #"\";
// make a folder to store the images in
string fileDirectory = root + #"\images\a4en\";
// create the folder if it does not exist
if (!System.IO.Directory.Exists(fileDirectory))
System.IO.Directory.CreateDirectory(fileDirectory);
WriteBytesToFile( fileDirectory + + procad.ForSaleID + ".png", imageBytes);
Yes i also try wkhtmltopdf c# wrapper but in pdf or image converting time my computer fan goin crayz. also i must upload server exe file and my hosting firm didnt support them

Can a PDF be converted to a vector image format that can be printed from .NET?

We have a .NET app which prints to both real printers and PDF, currently using PDFsharp, although that part can be changed if there's a better option. Most of the output is generated text or images, but there can be one or more pages that get appended to the end. That page(s) are provided by the end-user in PDF format.
When printing to paper, our users use pre-printed paper, but in the case of an exported PDF, we concatenate those pages to the end, since they're already in PDF format.
We want to be able to embed those PDFs directly into the print stream so they don't need pre-printed paper. However, there aren't really any good options for rendering a PDF to a GDI page (System.Drawing.Graphics).
Is there a vector format the PDF could be converted to by some external program, that could rendered to a GDI+ page without being degraded by conversion to a bitmap first?
In an article titled "How To Convert PDF to EMF In .NET," I have shown how to do this using our PDFOne .NET product. EMFs are vector graphics and you can render them on the printer canvas.
A simpler alternative for you is PDF overlay explained in another article titled "PDF Overlay - Stitching PDF Pages Together in .NET." PDFOne allows x-y offsets in overlays that allows you stitch pages on the edges. In the article cited here, I have overlaid the pages one over another by setting the offsets to zero. You will have set it to page width and height.
DISCLAIMER: I work for Gnostice.
Ghostscript can output PostScript (which is a vector file) which can be directly sent to some types of printers. For example, if you're using an LPR capable printer, the PS file can be directly set to that printer using something like this project: http://www.codeproject.com/KB/printing/lpr.aspx
There are also some commercial options which can print a PDF (although I'm not sure if the internal mechanism is vector or bitmap based), for example http://www.tallcomponents.com/pdfcontrols2-features.aspx or http://www.tallcomponents.com/pdfrasterizer3.aspx
I finally figured out that there is an option that addresses my general requirement of embedding a vector format into a print job, but it doesn't work with GDI based printing.
The XPS file format created by Microsoft XPS Writer print driver can be printed from WPF, using the ReachFramework.dll included in .NET. By using WPF for printing instead of GDI, it's possible to embed an XPS document page into a larger print document.
The downside is, WPF printing works quite a bit different, so all the support code that directly uses stuff in the Sytem.Drawing namespace has to be re-written.
Here's the basic outline of how to embed the XPS document:
Open the document:
XpsDocument xpsDoc = new XpsDocument(filename, System.IO.FileAccess.Read);
var document = xpsDoc.GetFixedDocumentSequence().DocumentPaginator;
// pass the document into a custom DocumentPaginator that will decide
// what order to print the pages:
var mypaginator = new myDocumentPaginator(new DocumentPaginator[] { document });
// pass the paginator into PrintDialog.PrintDocument() to do the actual printing:
new PrintDialog().PrintDocument(mypaginator, "printjobname");
Then create a descendant of DocumentPaginator, that will do your actual printing. Override the abstract methods, in particular the GetPage should return DocumentPages in the correct order. Here's my proof of concept code that demonstrates how to append custom content to a list of Xps documents:
public override DocumentPage GetPage(int pageNumber)
{
for (int i = 0; i < children.Count; i++)
{
if (pageNumber >= pageCounts[i])
pageNumber -= pageCounts[i];
else
return FixFixedPage(children[i].GetPage(pageNumber));
}
if (pageNumber < PageCount)
{
DrawingVisual dv = new DrawingVisual();
var dc = dv.Drawing.Append();
dc = dv.RenderOpen();
DoRender(pageNumber, dc); // some method to render stuff to the DrawingContext
dc.Close();
return new DocumentPage(dv);
}
return null;
}
When trying to print to another XPS document, it gives an exception "FixedPage cannot contain another FixedPage", and a post by H.Alipourian demonstrates how to fix it: http://social.msdn.microsoft.com/Forums/da/wpf/thread/841e804b-9130-4476-8709-0d2854c11582
private DocumentPage FixFixedPage(DocumentPage page)
{
if (!(page.Visual is FixedPage))
return page;
// Create a new ContainerVisual as a new parent for page children
var cv = new ContainerVisual();
foreach (var child in ((FixedPage)page.Visual).Children)
{
// Make a shallow clone of the child using reflection
var childClone = (UIElement)child.GetType().GetMethod(
"MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic
).Invoke(child, null);
// Setting the parent of the cloned child to the created ContainerVisual
// by using Reflection.
// WARNING: If we use Add and Remove methods on the FixedPage.Children,
// for some reason it will throw an exception concerning event handlers
// after the printing job has finished.
var parentField = childClone.GetType().GetField(
"_parent", BindingFlags.Instance | BindingFlags.NonPublic);
if (parentField != null)
{
parentField.SetValue(childClone, null);
cv.Children.Add(childClone);
}
}
return new DocumentPage(cv, page.Size, page.BleedBox, page.ContentBox);
}
Sorry that it's not exactly compiling code, I just wanted to provide an overview of the pieces of code necessary to make it work to give other people a head start on all the disparate pieces that need to come together to make it work. Trying to create a more generalized solution would be much more complex than the scope of this answer.
While not open source and not .NET native (Delphi based I believe, but offers a precompiled .NET library), Quick PDF can render a PDF to an EMF file which you could load into your Graphics object.

How to save a complete webpage using the built-in webbrowser in c#

Overall I am trying to write out a webpage to PDF. There is a web service that I can use to convert a file to pdf. So what I am trying to do is save out a webpage from the WebBrowser winforms control.
I have already tried writing it out the document stream but that just gives me the html of the page and not the images that are used with it.
Another way that I looked into, but have not been successful with, is trying to create an image of the WebBrowser document. I found some examples on the web that utilize the DrawToBitmap function but none of them have worked for me.
Any assistance would be grateful.
A long time ago I stumbled over this CodeProject-article 'Capture an HTML document as an image'
however, there is a new one (posted: 13 Feb 2010) 'HTML to Image in C#'
I haven't tested either of them but I think they should work!
You can take screenshots until you have the entire page using the Graphics.CopyFromScreen function.
// Get screen location of web browser
Rectangle rec = webBrowser1.RectangleToScreen(webBrowser1.ClientRectangle);
// create image to hold whats in view
Bitmap image = new Bitmap(rec.Width, rec.Height);
// get graphics to draw on image
Graphics g = Graphics.FromImage(image);
// Save into image
// From MSDN:
//public void CopyFromScreen(
// int sourceX,
// int sourceY,
// int destinationX,
// int destinationY,
// Size blockRegionSize
//)
g.CopyFromScreen(rec.X,rec.Y,0,0,rec.Size)
You may also want to remove the scrollbars so they aren't in your image:
webBrowser.ScrollBarsEnabled = false;
webBrowser.Document.Body.Style = "overflow:hidden;";
And then scroll down to take a shot of the next page:
webBrowser.Document.Window.ScrollTo(x,y);
To create the PDF, the program you're using will need the source code of the site. Wether you use the WebBrowser winforms control or something else to get that info, is of no real difference.
This code will get the source code of any site for you, presuming you don't need to upload stuff first:
string url = "some site";
string source = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using(StreamReader sr = new StreamReader(response.GetResponseStream()){
source = sr.ReadToEnd();
}

convert bitmap to image c# [duplicate]

this is how my code look now:
System.Drawing.Image objImage = System.Drawing.Image.FromFile(Server.MapPath("aaa.jpg"));
int height = objImage.Height;
int width = objImage.Width;
System.Drawing.Bitmap bitmapimage = new System.Drawing.Bitmap(objImage, width, height);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmapimage);
System.Drawing.Image bitmap2 = (System.Drawing.Image)Bitmap.FromFile(Server.MapPath("sem.png"));
g.DrawImage(bitmap2, (objImage.Width - bitmap2.Width) / 2, (objImage.Height - bitmap2.Height) / 2);
MemoryStream stream = new MemoryStream();
bitmapimage.Save(stream, ImageFormat.Jpeg);
String saveImagePath = Server.MapPath("ImagesMerge/") + "aaa.jpg";
bitmapimage.Save(saveImagePath);
imgBig.ImageUrl = saveImagePath;
The problem I have now is that the image is not displayed in browser, I don't understand why .
like jmaglasang said, I would suggest to you to use an ashx file and if you don't need to keep the image, just send the image stream directly to the http without saving it on the disk
so you only need to do something like
<img src="Handler.ashx?action=merge&image1=blah.jpg&image2=bloh.jpg">
look at this code for an example of how to send an image made in memory that does not exist on the drive
Bitmap is a subclass of Image, so there no need to convert Bitmap to Image. It already is...
Probably because saveImagePath will be a local path (such as c:\somepath\aaa.jpg) that is not reachable from the browser. You probably want to set the ImageUrl = "ImagesMerge/aaa.jpg" instead.
You can also try:
imgBig.ImageUrl = ResolveUrl(saveImagePath);
EDIT:
If saveImagePath is under the WebApplication Directory, doing some modifications on the directory structure i.e. modifying files, deleting and creating can cause the application pool to recycle, and once it reaches the maximum recycle count the application pool will be stopped causing "Server unavailable" error.
I would suggests to add/save/modify images on a separate directory (not under the Apps Directory) then create a Handler(ASHX) that will read the images, just an advice though.
MapPath will give you a physycal address, not a virtual address which is what the browser needs to get to the image.
You might be forgetting to set the Response.Headers. Check out the following example that shows how to create bar chart images and then display it on the screen:
http://www.highoncoding.com/Articles/399_Creating_Bar_Chart_Using__NET_Graphics_API.aspx

Categories