Scraping images from Website in delphi with twebbrowser - image

im trying to make a small tool which downloads all images from the site visited. It have to be made with twebbrowser component. The test site from my customer is Click. At the moment im selecting the pictures with getelementbyid but some of the pictures dont have a id. How can i adress the missing ones? Thanks alot

After the page is loaded, query the TWebBrowser.Document property for the IHTMLDocument2 interface, and then you can enumerate the elements of the IHTMLDocument2.images collection:
var
Document: IHTMLDocument2;
Images: IHTMLElementCollection;
Image: IHTMLImgElement;
I: Integer;
begin
Document := WebBrowser1.Document as IHTMLDocument2;
Images := Document.images;
For I := 0 to Images.length - 1 do
begin
Image := Images.item(I, '') as IHTMLImgElement;
// use Image as needed...
end;
end;
Note that this will only find images in HTML <img> tags. If you need to find images in <input type="image"> tags as well, you will have to enumerate the elements of the IHTMLDocument2.all collection looking for instances of the IHTMLInputElement interface whose type property is "image", eg:
var
Document: IHTMLDocument2;
Elements: IHTMLElementCollection;
Element: IHTMLElement;
Image: IHTMLImgElement;
Input: IHTMLInputElement;
I: Integer;
begin
Document := WebBrowser1.Document as IHTMLDocument2;
Elements := Document.all;
For I := 0 to Elements.length - 1 do
begin
Element := Elements.item(I, '') as IHTMLElement;
if Element is IHTMLImgElement then begin
Image := Element as IHTMLImgElement;
// use Image as needed...
end
else if Element is IHTMLInputElement then begin
Input := Element as IHTMLInputElement;
if Input.type = 'image' then
begin
// use Input as needed...
end;
end;
end;
end;

Instead of requesting a specific element by id, you can "walk" the document and look at each element using WebDocument.all.item(itemnum,'').
var
cAllElements: IHTMLElementCollection;
eThisElement: IHTMLElement;
WebDocument: IHTMLDocument2;
=======
cAllElements:=WebDocument.All
For iThisElement:=0 to cAllElements.num-1 do
begin
eThisElement:=cAllElements.item(iThisElement,'') as IHTMLElement;
// check out eThisElement and do what you want
end;
You would then look at the element .tagName for IMG, or do whatever assessment you need in order to determine if it is a picture and handle it as you did before.
Dan

Related

best way to duplicate teeChart to ReportBuilder for printing purposes

I have a TeeChart with 32 series and 6 custom axes. I need to print this chart, and we're using ReportBuilder everywhere else in our software to print charts (where there will be a printpreview on the screen first). We're using the CloneChart-method to have the Chart copied to ReportBuilder. With this chart we have the problem that the chart is not fully visible, like it is not stretching.
I also notice that the custom axes are not visible in ReportBuilder.
My goal is to have an exact copy of the TeeChart on the form, in ReportBuilder for printing purposes.
I tried copying the custom-axis, added the custom-axis in the ReportBuiled ppChart. Point is that the series are created at runtime. Hmmm, I need to link the series with the right custom axes or something.
I tried several other things in a search for streching, without success.
for i := 0 to aChartSource.SeriesCount - 1 do
begin
if aChartSource[i].Active then
begin
s := CloneChartSeries(aChartSource[i]);
s.ParentChart := AChartTarget.Chart;
s.GetVertAxis.Grid.Visible := (s.GetVertAxis.Grid.Visible and aShowGrid);
s.GetHorizAxis.Grid.Visible := (s.GetHorizAxis.Grid.Visible and aShowGrid);
s.Marks.Visible := (s.Marks.Visible and aShowMarks);
s.OnGetMarkText := GetMarkText;
for ii := 0 to AChartSource[i].Count -1 do
begin
if (s.ValueColor[ii] <> AChartSource[i].ValueColor[ii]) then
begin
s.ValueColor[ii] := AChartSource[i].ValueColor[ii];
vRedraw := TRUE;
end;
end;
end;
end;
{ Duplicate the axis }
for i := 0 to aChartSource.CustomAxes.Count - 1 do
begin
ppchrtKPI.Chart.CustomAxes.Add;
LAxis.
LAxis := ppchrtKPI.Chart.CustomAxes[ppchrtKPI.Chart.CustomAxes.Count-1];
LAxis.Assign(aChartSource.CustomAxes[i]);
end;
The ouput on the ReportBuilder's PrintPreview form is a chart with the series, but without the custom vertical axes. And the Teechart in ReportBuilder seems to be cropped; the series near the bottom X-axis are not visible
As said here, the problem with the custom axes sounds like the ticket #780 which was fixed in v2018.24.180321.

TimageList does not contain a member named GetBitmap

I want to load pictures from an ImageList to a TImage (mobile application, fmx). The TImage is part of my customstyle Listbox (LBItem.StylesData['myimage']). The standard approach would be ImageList.GetBitmap(). However the GetBitmap method gives me an error: 'TimageList does not contain a member named GetBitmap'. Any explanation or alternatives? Thanks in advance!
procedure TForm3.Button1Click(Sender: TObject);
var
i : Integer;
LBItem : TListBoxItem;
Bitmap : TBitMap;
begin
ListBox1.BeginUpdate;
ListBox1.Items.Clear;
Bitmap := TBitMap.Create;
try
for i := 0 to 3 do begin
LBItem := TListBoxItem.Create(nil);
LBItem.Parent := ListBox1;
LBItem.StyleLookup := 'mystyle';
LBItem.StylesData['mylabel'] := 'Some text...';
//Bitmap.LoadFromFile('D:\Koala.jpg');
ImageList1.GetBitmap(i, Bitmap);
LBItem.StylesData['myimage']:= Bitmap;
end;
finally
ListBox1.EndUpdate;
end;
end;
Assuming you have an TImage with name Image1, a TImageList with name ImageList1 and at least one entry in the list with image for scale 1.0 called Image1Hover, then you can use the following example to load a "hover picture" in the OnEnter event of Image1:
procedure TForm1.Image1MouseEnter(Sender: TObject);
var
Item: TCustomBitmapItem;
Size: TSize;
begin
ImageList1.BitmapItemByName('Image1Hover', Item, Size);
Image1.Bitmap := Item.MultiResBitmap.Bitmaps[1.0];
end;
This answer is translate from fire-monkey.ru
Use ImageList1.Bitmap(Size, Index);. The size is in physical pixels, i.e. we consider the scale independently (this method knows nothing about the scale of the canvas). This function selects the most appropriate size of the image that is available.
So, your code should look something like this:
LBItem.StylesData['myimage'] := ImageList1.Bitmap(
TSizeF.Create(myImageWidth * Canvas.Scale, myImageHeight * Canvas.Scale),
i);
// Not sure of the correctness of this assignment to 'myimage'
Note 1 All the bitmaps obtained in the 'ImageList1.Bitmap` are stored in the imagelist cache. So don't release them.
Note 2 ListBox has internal mechanism to interact with ImageList. Try to use icon: TImage style item and LBItem.ImageIndex property, without load bitmaps.
In FMX you don't need any additional coding for that, just use TGlyph instead of TImage if you want to display images directly form ImageList.
example :
Glyph1.ImageIndex := i;

Picture Database, TDBImages, TImageList, Delphi

I am writing a programme that shows a picture(map). when you click on a part of the picture it must Zoom in. There are 26 pictures in total(Including main picture). I Want to load those pictures into Delphi and replace Image1(Whole_map.jpg) with Amusement_park.jpg.
I want to use the good quality jpg not bitmaps :(
*Is it possible to load those 26 images into TImageList and still use the images with its quality
or
*Can i save the images in some sort of Database and load it into Delphi
Loading images and converting to bitmap
doesn't help because i don't want to use bitmaps.
I also don't want to use any 3rd party components because this program must run on default Delphi 2010.
As mentioned in my coment you can create an array of TJPEGImage objects to store the images.
You do this like so:
//Global array for storing images
var Images: Array [1..26] of TJPEGImage;
implemenetation
...
procedure TForm1.FormCreate(Sender: TObject);
var I: Integer;
begin
for I := 1 to 26 do
begin
//Since TJPEGIMage is a class we first need to create each one as array only
//stores pointer to TJPEGImage object and not the object itself
Images[I] := TJPEGImage.Create;
//Then we load Image data from file into each TJPEGImage object
//If file names are not numerically ordered you would probably load images
//later and not inside this loop. This depends on your design
Images[I].LoadFromFile('D:\Image'+IntToStr(I)+'.jpg');
end;
end;
As you see in source coments the array only stores pointers to TJPEGImage objects and not the TJPEGImage objects themself. So don't forget to create them before trying to load any image data to them. Failing to do so will result in Access Violation.
Also becouse you have created these TJPEGImage objects by yourself you also need to free them by yourself to avoid posible memory leaks
procedure TForm1.FormDestroy(Sender: TObject);
var I: Integer;
begin
for I := 1 to 26 do
begin
Images[I].Free;
end;
end;
In order to show these stored images in your TImage component use this
//N is array index number telling us which array item stores the desired image
Image1.Picture.Assign(Images[N]);
Second approach that you can use
Now since TJPEGImage are classed objects you could also use TObjectList to store pointers to them.
In such case creation code would look like this
procedure TForm1.FormCreate(Sender: TObject);
var I: Integer;
Image: TJPEGImage;
for I := 1 to NumberOfImages do
begin
//Create TObject list with AOwnsObjects set to True means that destroying
//the object list will also destroy all of the objects it contains
//NOTE: On ARC compiler destroying TObjectList will only remove the reference
//to the objects and they will be destroyed only if thir reference count
//drops to 0
Images := TObjectList.Create(True);
//Create a new TJPEGImage object
Image := TJPEGImage.Create;
//Load image data into it from file
Image.LoadFromFile('Image'+IntToStr(I)+'.jpg');
//Add image object to our TObject list to store reference to it for further use
Images.Add(Image);
end;
end;
You would now show these images like so
//Note becouse first item in TObject list has index of 0 you need to substract 1
//from your ImageNumber
Image1.Picture.Assign(TJPEGImage(Images[ImageNumber-1]));
Since we set TObjectList to own our TJPEGImage objects we can quickly destroy all of them like so
//NOTE: On ARC compiler destroying TObjectList will only remove the reference
//to the objects and they will be destroyed only if thir reference count
//drops to 0
Images.Free;

Firemonkey style in Listbox - retrieving data

I'm trying to get information from a Tlistbox in Firemonkey XE5 but it has an associated style where each item in the listbox includes an image, a memo and some buttons.
When clicking on the button inside the listbox style, I can get information from that item.
I want to get information from a the memo box in the listbox separately. Previously, I would have got the text from item 1 by using the following code:
NewString:=ListBox1.items[1];
However, now each item in the listbox has more than one piece of information.
I can add a new Listbox item using the code as follows:
var Item: TListBoxItem;
begin
Item := TListBoxItem.Create(nil);
Item.Parent := ListBox1;
Item.StyleLookup := 'PlaylistItem';
Item.StylesData['Memo1']:='test text';
But, how do I read just the memo box of a particular item
Thanks
Aman
Update.
The solution is
Tempstr:=ListBox1.ItemByIndex(1).StylesData['Memo1'].AsString;
I'm now trying to work out how to get an image out as there isn't a AsImage or AsBitmap suffix.
I would advise subclassing TListBoxItem, then adding properties and methods to get/set the data from the style objects using FindStyleResource,
class TMemoListBoxItem = class(TListBoxItem)
protected
function GetMemoText: String;
procedure SetMemoText(const Text: String);
published
property MemoText: String read GetMemoText write SetMemoText;
end;
function TMemoListBoxItem.GetMemoText: String;
var O: TFMXObject;
begin
O := FindStyleResource('Memo1');
if O is TMemo then
Result := TMemo(O).Text
else
Result := '';
end;
procedure TMemoListBoxItem.SetMemoText(const Text: String);
var O: TFMXObject;
begin
O := FindStyleResource('Memo1');
if O is TMemo then
TMemo(O).Text := Text;
end;
And continue likewise for your other data.

Why Do The Cell Properties In A TStringGrid Column Title Cells Change When A Grid Is Loaded?

I am running Lazarus v0.9.30 (32 bit compiler).
This question is an extension of a previous question of mine.
The previous question revolved around how to change the orientation of text in the TGridColumns objects that I loaded at runtime into a standard TStringGrid. The solution involved overriding the DrawCellText event of the string grid.
My question is this. When I attempt to load the TStringGrid what I find is that the text orientation remains the same but the column cell height changes back to the default height.
The code I use to load the grid is shown below.
procedure TTmMainForm.vLoadWorldScoutGrid;
var
aMember : TTmMember;
anIndex1: integer;
anIndex2: integer;
begin
//Clear the string grid and set the row count to 1 to take into account the fixed row.
SgWorldScout.Clear;
SgWorldScout.RowCount := 1;
for anIndex1 := 0 to Pred(FManager.Members.Count) do
begin
//Add a new row to the string grid.
SgMembers.RowCount := SgMembers.RowCount + 1;
//Get the TTmMember object from the collection.
aMember := TTmMember(FManager.Members.Items[anIndex1]);
//Populate the row cells in the string grid.
SgMembers.Cells[0, SgMembers.RowCount - 1] := aMember.stMemberNumber;
SgMembers.Cells[1, SgMembers.RowCount - 1] := aMember.stPatrol;
SgMembers.Cells[2, SgMembers.RowCount - 1] := aMember.stSurname;
SgMembers.Cells[3, SgMembers.RowCount - 1] := aMember.stFirstName;
//Add the TTmMember object to every row cell.
for anIndex2 := 0 to SgMembers.ColCount - 1 do
SgMembers.Objects[anIndex2, SgMembers.RowCount - 1] := aMember;
end; {for}}
vSetWorldScoutGridPushbuttons;
end;
I suspect that when I call 'SgWorldScout.Clear' that the properties of the string grid cells may get reset / modified as the default DrawCellText event gets called, which would explain the change in cell height. Not sure why the text orientation doesn't change either. Would someone be able to explain the behaviour of the DrawCellText event and why I am seeing this?
The Clear sets the RowCount and ColCount to 0 as you suspected. Then it's quite logical that the RowHeights is cleared too, because when you have RowCount set to 0 there are no heights to store. If you want to clear and add only the non fixed rows then simply set only the RowCount to 1 without clearing the whole grid. So modify your code this way:
procedure TTmMainForm.vLoadWorldScoutGrid;
var
aMember : TTmMember;
anIndex1: integer;
anIndex2: integer;
begin
// set the row count to 1 to keep the fixed row along with its settings
SgWorldScout.RowCount := 1;
for anIndex1 := 0 to Pred(FManager.Members.Count) do
...
end;

Resources