Basically I have a string that will looks like this :
<p>HELLO <span style='color:red'>WORLD</span></p>
How would I go about converting this string of HTML into an image? (Bitmap, PNG, etc.)
Does anyone know of any VB.NET tools that will do this? Since this is a string, it cannot read from the DOM as it technically isn't part of the DOM.
My overall purpose is to create an ASPX page that will reading a field containing some HTML from a database, converting that HTML into an image, and the steaming that image on the page by spoofing the ContentType to "image/png", thereby creating a dynamic image that I can use in an RDLC file.
The reason we're displaying HTML as an image in an RDLC file is simply because all of our reports are using ReportViewer version 9.0 which does not support interpreted HTML. Creating a dynamic image based on HTML allows us to display the "formatting" that we want to show directly in the report, which we could not do otherwise, not with such flexibility anyways.
You can render html on the screen using the WebBrowser control, then copy the screen bitmap to am image file.
WebBrowser1.DocumentText = "<p>HELLO <span style='color:red'>WORLD</span></p>"
This code ALMOST works. You need to click the button twice, and it only works properly on the second click. There is a timing issue with grabbing the rendered page you will need to solve. Also, the form must be in front, as it is a screengrab, so only grabs the visible portion of the screen.
Option Strict On
Imports System.Drawing
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strHTML As String = "<p>HELLO <span style='color:red'>WORLD</span></p>"
WebBrowser1.DocumentText = strHTML
Dim bmp As Bitmap = GetControlImage(WebBrowser1)
Dim strFilename As String = "C:\Junk\Junk.png"
bmp.Save(strFilename, System.Drawing.Imaging.ImageFormat.Png)
End Sub
Private Declare Function BitBlt _
Lib "gdi32.dll" ( _
ByVal hdcDest As IntPtr, _
ByVal x As Int32, _
ByVal y As Int32, _
ByVal Width As Int32, _
ByVal Height As Int32, _
ByVal hdcSrc As IntPtr, _
ByVal xSrc As Int32, _
ByVal ySrc As Int32, _
ByVal dwRop As Int32 _
) As Boolean
Private Const SRCCOPY As Integer = &HCC0020
Function GetControlImage(ctl As Control) As Bitmap
Dim grpInput As Graphics = ctl.CreateGraphics
' Create a compatible bitmap and get its Graphics object
Dim bmpOutput As New Bitmap(ctl.ClientRectangle.Width, ctl.ClientRectangle.Height, grpInput) 'formImage = New Bitmap(frmForm.ClientRectangle.Width, frmForm.ClientRectangle.Height, formGraphics)
Dim grpOutput As Graphics = Graphics.FromImage(bmpOutput)
' Get the target and source device context handles (hDC)
Dim sourceDC As IntPtr = grpInput.GetHdc
Dim targetDC As IntPtr = grpOutput.GetHdc
' Copy the control's client area
BitBlt(targetDC, 0, 0, ctl.ClientRectangle.Width, ctl.ClientRectangle.Height, sourceDC, ctl.ClientRectangle.X, ctl.ClientRectangle.Y, SRCCOPY)
' Release DCs and dispose objects
grpInput.ReleaseHdc(sourceDC)
grpInput.Dispose()
grpOutput.ReleaseHdc(targetDC)
grpOutput.Dispose()
Return bmpOutput
End Function
End Class
wkhtmltopdf also has a a wkhtmltoimage.exe that you can utilize, try http://wkhtmltopdf.org/
Adding "Application.DoEvents" immediately before the instruction "Dim bmp As Bitmap = GetControlImage(WebBrowser1)" seems to solve the problem of having to press the button twice.
Related
I have some issues downloading an image (or zip sometimes) from a webpage.
I've checked a few forums about the topic where most of the time they suggest using the URLDownloadToFile function.
I tried to apply it but it doesn't seem to work.
Here's an example of the type of webpage I'm dealing with :
The type here is jpg but sometimes it can be a zip.
For the jpg case, I have two ways to do it:
Click on the View button, which will open a new webpage containing 1 image only, selecting that webpage and somehow dowloading the image, which I don't manage to do.
(There is a "Save Picture As" when you rigth click a picture manually, but how to access to this with VBA ? ) :
objIE.document.frames(1).frames(1).document.getElementById("notPrintable").document.getElementsByName("view")(0).Click 'This clicks on the View Button
attachment_url = "https://pumapgf-row.bmwgroup.net/puma/case/showfile.do?selectedIndex=" & elem_id & "&filename=" & elem_name & "%20%7C%20jpg%20%7C%20" & end_url ' this is the url of the new webpage which is opened when I click the view button
Set objIE = IEWindowFromLocation(attachment_url) ' I select the new webpage
Set IEDoc = objIE.document ' set document on it
The html from this new webpage in the case it's a jpg of course) looks like this:
What I tried to do then but unsuccessfully is to use the URLDownloadToFile function this way
Dim myImages As IHTMLElementCollection
Set myImages = IEDoc.getElementsByTagName("img")
returnValue = URLDownloadToFile(0, myImages(0).href, "P:\Alex\ABC.img", 0, 0)
Whether I create or not a such called file before I run the code, it doesn't make any difference. I also tried with .jpg, .img, .png.
myImages(0).href ends like this :
So I don't know if the fact that .href doesn't end with something like .jpg or .img is an issue.
Click on the Save As button : valid for both jpg and zip files, so would be a better solution. I manage to click on it of course, but the issue comes from the fact that Internet displays this and I have no idea how to deal with it.
Any idea how to do this ?
EDIT : Here is the properties window of the image
Assuming that you have a valid download URL (which I can't test based on the site in your question), all you should need to do to test if a file is a jpg is to download it and check for the presence of the JPEG file header:
Public Function FileIsJpg(filepath As String) As Boolean
Dim handle As Long
handle = FreeFile
Open filepath For Binary As #handle
Dim header As Integer
Get #handle, , header
'Note the byte order.
If header = &HD8FF Then
Get #handle, , header
If header = &HE0FF Or header = &H1FF Then
FileIsJpg = True
End If
End If
Close #handle
End Function
Note that for your usage, this will need error handling because of the possibility that URLDownloadToFile still has the file open. I'm assuming that you have some sort of wait mechanism in place (it's a non-blocking function). If not, you need to either use the native callback mechanisms or take a guess and used Application.Wait or something similar.
Example usage:
Private Declare Function URLDownloadToFile Lib "urlmon" _
Alias "URLDownloadToFileA" (ByVal pCaller As Long, _
ByVal szURL As String, ByVal szFileName As String, _
ByVal dwReserved As Long, ByVal lpfnCB As Long) As Long
Private Const S_OK As Long = 0
Sub Examples()
Const TestJpgUrl As String = "https://www.gstatic.com/webp/gallery/1.jpg"
Const TestPngUrl As String = "https://www.gstatic.com/webp/gallery3/1.png"
Dim target As String
target = Environ$("TEMP") & "\test.png"
If URLDownloadToFile(0, TestPngUrl, target, 0, 0) = S_OK Then
'Wait for download to complete - a callback function would be better.
Application.Wait Now + TimeSerial(0, 0, 1)
MsgBox target & ": " & FileIsJpg(target)
End If
Kill target
target = Environ$("TEMP") & "\test.jpg"
If URLDownloadToFile(0, TestJpgUrl, target, 0, 0) = S_OK Then
Application.Wait Now + TimeSerial(0, 0, 1)
MsgBox target & ": " & FileIsJpg(target)
End If
Kill target
End Sub
Note that you can also explicitly test for zip files in a similar way, but I'll leave that as an exercise for the reader.
Hello i generate HTML input text controls with a string builder and put it inside a div from Code Behind.
Then i also need to assign values to these generated input & update database if values change by the user.
The problem is Code Behind can't find the generated from String builder HTML Input-text Controls
You can see code example below:
Public Class WebForm1
Inherits System.Web.UI.Page
Private Sub WebForm1_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
Dim strB As New StringBuilder
For i = 0 To 5
strB.Append("<input type=""text"" value="""" runat=""server"" id=position_" & i & "/>")
Next
wraper.InnerHtml = strB.ToString
strB.Clear()
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For i = 0 To 5
Dim a As HtmlInputText
a = Me.Page.FindControl("position_" & i)
a.Value = "test"
Next
End Sub
End Class
That's because you're not creating controls, you're creating strings without a context.
You might want to look at these questions:
ASP: runat=server for dynamic control
Dynamically Created Controls losing data after postback
I'd like to use a .png as a custom icon in the Access 2007 ribbon.
Here's what I've tried so far:
I am able to load .bmp's and .jpg's as custom images without any problem. I can load .gif's, but it doesn't seem to preserve the transparency. I can't load .png's at all. I'd really like to use .png's to take advantage of the alpha-blending that is not available in the other formats.
I found a similar question on SO, but that just deals with loading custom icons of any kind. I am specifically interested in .png's. There is an answer from Albert Kallal to that question that links to a class module he had written that appears to do exactly what I want:
meRib("Button1").Picture = "HappyFace.png"
Unfortunately, the link in that answer is dead.
I also found this site which offers a download of a 460 line module full of dozens of API calls to get support for transparent icons. Before I go that route I wanted to ask the experts here if they know of a better way.
I know .png is pretty new-fangled and all, but I'm hoping the Office development folks slipped in some native support for the format.
Here is what I am currently using. Albert Kallal has a more full-fledged solution for Access 2007 ribbon programming that does a lot more than just load .png's. I am not using it yet, but it's worth checking out.
For those who are interested, here is the code that I am using. I believe this is pretty close to the minimum required for .png support. If there's anything extraneous here, let me know and I'll update my answer.
Add the following to a standard code module:
Option Compare Database
Option Explicit
'================================================================================
' Declarations required to load .png's in Ribbon
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
Private Type PICTDESC
Size As Long
Type As Long
hPic As Long
hPal As Long
End Type
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Declare Function GdiplusStartup Lib "GDIPlus" (token As Long, _
inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
Private Declare Function GdipCreateBitmapFromFile Lib "GDIPlus" (ByVal filename As Long, bitmap As Long) As Long
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" (ByVal bitmap As Long, _
hbmReturn As Long, ByVal background As Long) As Long
Private Declare Function GdipDisposeImage Lib "GDIPlus" (ByVal image As Long) As Long
Private Declare Function GdiplusShutdown Lib "GDIPlus" (ByVal token As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32.dll" (PicDesc As PICTDESC, _
RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
'================================================================================
Public Sub GetRibbonImage(ctl As IRibbonControl, ByRef image)
Dim Path As String
Path = Application.CurrentProject.Path & "\Icons\" & ctl.Tag
Set image = LoadImage(Path)
End Sub
Private Function LoadImage(ByVal strFName As String) As IPicture
Dim uGdiInput As GdiplusStartupInput
Dim hGdiPlus As Long
Dim hGdiImage As Long
Dim hBitmap As Long
uGdiInput.GdiplusVersion = 1
If GdiplusStartup(hGdiPlus, uGdiInput) = 0 Then
If GdipCreateBitmapFromFile(StrPtr(strFName), hGdiImage) = 0 Then
GdipCreateHBITMAPFromBitmap hGdiImage, hBitmap, 0
Set LoadImage = ConvertToIPicture(hBitmap)
GdipDisposeImage hGdiImage
End If
GdiplusShutdown hGdiPlus
End If
End Function
Private Function ConvertToIPicture(ByVal hPic As Long) As IPicture
Dim uPicInfo As PICTDESC
Dim IID_IDispatch As GUID
Dim IPic As IPicture
Const PICTYPE_BITMAP = 1
With IID_IDispatch
.Data1 = &H7BF80980
.Data2 = &HBF32
.Data3 = &H101A
.Data4(0) = &H8B
.Data4(1) = &HBB
.Data4(2) = &H0
.Data4(3) = &HAA
.Data4(4) = &H0
.Data4(5) = &H30
.Data4(6) = &HC
.Data4(7) = &HAB
End With
With uPicInfo
.Size = Len(uPicInfo)
.Type = PICTYPE_BITMAP
.hPic = hPic
.hPal = 0
End With
OleCreatePictureIndirect uPicInfo, IID_IDispatch, True, IPic
Set ConvertToIPicture = IPic
End Function
Then, if you don't already have one, add a table named USysRibbons. (NOTE: Access treats this table as a system table, so you'll have to show those in your nav pane by going to Access Options --> Current Database --> Navigation Options and make sure 'Show System Objects' is checked.) Then add these attributes to your control tag:
getImage="GetRibbonImage" tag="Acq.png"
For example:
<button id="MyButtonID" label="Do Something" enabled="true" size="large"
getImage="GetRibbonImage" tag="MyIcon.png" onAction="MyPublicSub"/>
I'm trying to make an executable in VS2008 that will read a webpage source code using a vb.NET function into a string variable. The problem is that the page is not *.html but rather *.aspx.
I need a way to execute the aspx and get the displayed html into a string.
The page I want to read is any page of this type: http://www.realtor.ca/PropertyDetails.aspx?PropertyID=9620716
I have tried the following code, which works properly for html pages, but generates the wrong source code with "access denied" for the page title when I pass in the above aspx page.
Dim myReq As WebRequest = WebRequest.Create(url)
Dim myWebResponse As WebResponse = myReq.GetResponse()
Dim dataStream As Stream = myWebResponse.GetResponseStream()
Dim reader As New StreamReader(dataStream, System.Text.Encoding.UTF8)
Dim responseFromServer As String = reader.ReadToEnd()
Any suggestions or ideas?
I get the same thing while running wget from the command line:
wget http://www.realtor.ca/PropertyDetails.aspx?PropertyID=9620716
I guess the server is relying on that something is set in the browser before the response is delivered, e.g. a cookie. You might want to try using a WebBrowser control (you don't have to have it visible) in the following way (this works):
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler WebBrowser1.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf DocumentCompletedHandler)
WebBrowser1.Navigate("http://www.realtor.ca/PropertyDetails.aspx?PropertyID=9620716")
End Sub
Private Sub DocumentCompletedHandler(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
Console.WriteLine(WebBrowser1.DocumentText)
End Sub
End Class
Can we change printer settings at run time in SSRS 2008?
For example, if a report is on Legal size paper but I want to print on A4 Landscape or Legal at run time, depending on a parameter which is passed in.
The answer is that NO, you cannot unless you write your own solution. The report viewer control does not support this. Your options are either to create a custom printing solution, OR to manipulate your RDLC file to modify the settings manually.
If you decide to modify the RDLC file on the fly, you need to load the RDLC file into memory, modify the XML file to specify the new page settings, then reload the RDLC file in the report viewer.
If you decide to do manual printing, here is some VB.NET code to help you get started:
Dim m_pageSettings As PageSettings 'Stores page settings for printout
Dim m_currentPage As Integer 'Used for index of pages
Private m_pages As New List(Of Stream)() 'Stores a stream for each pages
'Event fires when printDocument starts printing - reset page index to zero
Private Sub PrintDocument1_BeginPrint(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint
m_currentPage = 0
End Sub
'Function that prints all the pages included in the report
Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
Dim pageToPrint As Stream = m_pages(m_currentPage)
pageToPrint.Position = 0
Dim pageMetaFile As Metafile = New Metafile(pageToPrint) 'create an image(metafile) of the report page
Using (pageMetaFile)
'Create a rectangle the size of our report - include margins
' Dim adjustedRect As Rectangle = New Rectangle( _
' e.PageBounds.Left - CType(e.PageSettings.HardMarginX, Integer), _
' e.PageBounds.Top - CType(e.PageSettings.HardMarginY, Integer), _
' e.PageBounds.Width, _
' e.PageBounds.Height)
Dim adjustedRect As Rectangle = New Rectangle( _
e.PageBounds.Left, _
e.PageBounds.Top, _
e.PageBounds.Width, _
e.PageBounds.Height)
e.Graphics.FillRectangle(Brushes.White, adjustedRect) 'Fill rectangle with WHITE background
e.Graphics.DrawImage(pageMetaFile, adjustedRect) 'Draw report in rectangle - this will be printed
m_currentPage = m_currentPage + 1
e.HasMorePages = m_currentPage < m_pages.Count 'If more pages are left - keep processing
End Using
End Sub
'Event fires when PrintDocument queries for PageSettings. Return a copy of m_pagesettings.
Private Sub PrintDocument1_QueryPageSettings(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles PrintDocument1.QueryPageSettings
e.PageSettings = CType(m_pageSettings.Clone, PageSettings)
End Sub
'Render the report in a EMF - This function creates metafiles(images) of each page in the report
Private Sub RenderAllLocalReportPages(ByVal localReport As LocalReport)
Dim deviceInfo As String = CreateEMFDeviceInfo() 'Enhanced MetaFile
Dim warnings As Warning() = Nothing
m_pages.Clear()
localReport.Render("IMAGE", deviceInfo, AddressOf LocalReportCreateStreamCallback, warnings)
End Sub
'Callback function used with RenderAllLocalReportPages
Private Function LocalReportCreateStreamCallback(ByVal name As String, ByVal extension As String, ByVal encoding As Encoding, ByVal mimeType As String, ByVal willSeek As Boolean) As Stream
Dim stream As New MemoryStream()
m_pages.Add(stream)
Return stream
End Function
Private Function CreateEMFDeviceInfo() As String
Dim paperSize As PaperSize = m_pageSettings.PaperSize
Dim margins As Margins = m_pageSettings.Margins
'The device info string defines the page range to print as well as the size of the page.
'A start and end page of 0 means generate all pages.
Return String.Format(CultureInfo.InvariantCulture, "<DeviceInfo><OutputFormat>emf</OutputFormat><StartPage>0</StartPage><EndPage>0</EndPage><MarginTop>{0}</MarginTop><MarginLeft>{1}</MarginLeft><MarginRight>{2}</MarginRight><MarginBottom>{3}</MarginBottom><PageHeight>{4}</PageHeight><PageWidth>{5}</PageWidth></DeviceInfo>", ToInches(margins.Top), ToInches(margins.Left), ToInches(margins.Right), ToInches(margins.Bottom), ToInches(paperSize.Height), ToInches(paperSize.Width))
End Function
'Convert report printing size to inches
Private Shared Function ToInches(ByVal hundrethsOfInch As Integer) As String
Dim inches As Double = hundrethsOfInch / 100.0R
Return inches.ToString(CultureInfo.InvariantCulture) & "in"
End Function
Hopefully this helps.