mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-03 02:43:40 +02:00
600 lines
17 KiB
ObjectPascal
600 lines
17 KiB
ObjectPascal
{
|
|
Reads a HTML Document
|
|
|
|
License: The same modified LGPL as the Free Pascal RTL
|
|
See the file COPYING.modifiedLGPL for more details
|
|
|
|
AUTHORS: Felipe Monteiro de Carvalho
|
|
}
|
|
unit htmlvectorialreader;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, math, contnrs,
|
|
fpimage, fpcanvas, laz2_xmlread, laz2_dom, fgl,
|
|
// image data formats
|
|
fpreadpng,
|
|
// HTML can contain SVG
|
|
svgvectorialreader,
|
|
fpvectorial, fpvutils, lazutf8, TypInfo;
|
|
|
|
type
|
|
{ TvHTMLVectorialReader }
|
|
|
|
TvHTMLVectorialReader = class(TvCustomVectorialReader)
|
|
private
|
|
FPointSeparator, FCommaSeparator: TFormatSettings;
|
|
//
|
|
function GetTextContentFromNode(ANode: TDOMNode): string;
|
|
//
|
|
function ReadEntityFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
function ReadHeaderFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
procedure ReadParagraphFromNode(ADest: TvParagraph; ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument);
|
|
function ReadSVGFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
function ReadMathFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
function ReadTableFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
function ReadTableRowNode(ATable: TvTable; ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
function ReadUListFromNode(ANode: TDOMNode; AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
public
|
|
{ General reading methods }
|
|
constructor Create; override;
|
|
Destructor Destroy; override;
|
|
procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); override;
|
|
procedure ReadFromXML(Doc: TXMLDocument; AData: TvVectorialDocument);
|
|
class function IsSupportedRasterImage(AFileName: string): Boolean;
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
// SVG requires hardcoding a DPI value
|
|
|
|
// The Opera Browser and Inkscape use 90 DPI, so we follow that
|
|
|
|
// 1 Inch = 25.4 milimiters
|
|
// 90 inches per pixel = (1 / 90) * 25.4 = 0.2822
|
|
// FLOAT_MILIMETERS_PER_PIXEL = 0.3528; // DPI 72 = 1 / 72 inches per pixel
|
|
|
|
FLOAT_MILIMETERS_PER_PIXEL = 5*0.2822; // DPI 90 = 1 / 90 inches per pixel => Actually I changed the value by this factor! Because otherwise it looks ugly!
|
|
FLOAT_PIXELS_PER_MILIMETER = 1 / FLOAT_MILIMETERS_PER_PIXEL; // DPI 90 = 1 / 90 inches per pixel
|
|
|
|
{ TvHTMLVectorialReader }
|
|
|
|
function TvHTMLVectorialReader.GetTextContentFromNode(ANode: TDOMNode): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := '';
|
|
for i := 0 to ANode.ChildNodes.Count-1 do
|
|
begin
|
|
if ANode.ChildNodes.Item[i] is TDOMText then
|
|
Result := ANode.ChildNodes.Item[i].NodeValue;
|
|
end;
|
|
end;
|
|
|
|
// if something is returned, it will be added to the base document
|
|
// if nothing is returned, either nothing was written, or it was already added
|
|
function TvHTMLVectorialReader.ReadEntityFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
lEntityName: DOMString;
|
|
lPara: TvParagraph;
|
|
begin
|
|
Result := nil;
|
|
lEntityName := LowerCase(ANode.NodeName);
|
|
case lEntityName of
|
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6': Result := ReadHeaderFromNode(ANode, AData, ADoc);
|
|
'p':
|
|
begin
|
|
lPara := AData.AddParagraph();
|
|
ReadParagraphFromNode(lPara, ANode, AData, ADoc);
|
|
Result := nil;
|
|
end;
|
|
'svg': Result := ReadSVGFromNode(ANode, AData, ADoc);
|
|
'math': Result := ReadMathFromNode(ANode, AData, ADoc);
|
|
'table': Result := ReadTableFromNode(ANode, AData, ADoc);
|
|
'br':
|
|
begin
|
|
AData.AddParagraph().AddText(LineEnding);
|
|
Result := nil;
|
|
end;
|
|
'ul': Result := ReadUListFromNode(ANode, AData, ADoc);
|
|
end;
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadHeaderFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
CurParagraph: TvParagraph;
|
|
lText: TvText;
|
|
lTextStr: string;
|
|
lHeaderType: DOMString;
|
|
begin
|
|
Result := nil;
|
|
CurParagraph := AData.AddParagraph();
|
|
CurParagraph.Style := ADoc.StyleTextBody;
|
|
lTextStr := ANode.FirstChild.NodeValue;
|
|
lText := CurParagraph.AddText(lTextStr);
|
|
lHeaderType := LowerCase(ANode.NodeName);
|
|
case lHeaderType of
|
|
'h1': lText.Style := ADoc.StyleHeading1;
|
|
'h2': lText.Style := ADoc.StyleHeading2;
|
|
'h3': lText.Style := ADoc.StyleHeading3;
|
|
'h4': lText.Style := ADoc.StyleHeading4;
|
|
'h5': lText.Style := ADoc.StyleHeading5;
|
|
'h6': lText.Style := ADoc.StyleHeading6;
|
|
end;
|
|
end;
|
|
|
|
procedure TvHTMLVectorialReader.ReadParagraphFromNode(ADest: TvParagraph; ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument);
|
|
var
|
|
lText: TvText = nil;
|
|
lTextSeparatedByBR: Boolean = False;
|
|
lTextStr: string;
|
|
lCurNode: TDOMNode;
|
|
lNodeName, lNodeValue, lAttrName, lAttrValue: DOMString;
|
|
lCurAttr: TDOMNode;
|
|
lRasterImage: TvRasterImage;
|
|
lEmbVecImg: TvEmbeddedVectorialDoc = nil;
|
|
i: Integer;
|
|
lWidth, lHeight: Double;
|
|
lAltText: string;
|
|
// xlink:href
|
|
lx, ly, lw, lh: Double;
|
|
lImageDataParts: TStringList;
|
|
lImageDataBase64: string;
|
|
lImageData: array of Byte;
|
|
lImageDataStream: TMemoryStream;
|
|
lImageReader: TFPCustomImageReader;
|
|
begin
|
|
ADest.Style := ADoc.StyleTextBody;
|
|
|
|
lCurNode := ANode.FirstChild;
|
|
while Assigned(lCurNode) do
|
|
begin
|
|
lNodeName := LowerCase(lCurNode.NodeName);
|
|
lNodeValue := lCurNode.NodeValue;
|
|
|
|
if (lCurNode is TDOMText) and not lTextSeparatedByBR then
|
|
begin
|
|
lTextStr := lNodeValue;
|
|
lText := ADest.AddText(lTextStr);
|
|
lCurNode := lCurNode.NextSibling;
|
|
Continue;
|
|
end
|
|
// If after a text we get a <br /> or a TDOMText, merge them
|
|
else if (lNodeName = 'br') and (lText <> nil) then
|
|
begin
|
|
lTextSeparatedByBR := True;
|
|
lCurNode := lCurNode.NextSibling;
|
|
Continue;
|
|
end
|
|
else if (lCurNode is TDOMText) and lTextSeparatedByBR then
|
|
begin
|
|
lTextStr := lNodeValue;
|
|
lText.Value.Add(lTextStr);
|
|
lCurNode := lCurNode.NextSibling;
|
|
Continue;
|
|
end;
|
|
|
|
// reset text merging
|
|
lText := nil;
|
|
lTextSeparatedByBR := False;
|
|
|
|
case lNodeName of
|
|
// <image width="100" height="100" xlink:href="data:image/png;base64,UgAAA....QSK5CYII="/>
|
|
// <img src="images/noimage.gif" width="100" height="100" alt="No image" />
|
|
'img', 'image':
|
|
begin
|
|
lRasterImage := nil;
|
|
lEmbVecImg := nil;
|
|
lWidth := -1;
|
|
lHeight := -1;
|
|
lAltText := '';
|
|
|
|
for i := 0 to lCurNode.Attributes.Length - 1 do
|
|
begin
|
|
lAttrName := lCurNode.Attributes.Item[i].NodeName;
|
|
lAttrValue := lCurNode.Attributes.Item[i].NodeValue;
|
|
|
|
case lAttrName of
|
|
'alt':
|
|
begin
|
|
lAltText := lAttrValue;
|
|
end;
|
|
'src':
|
|
begin
|
|
lAttrValue := lCurAttr.NodeValue;
|
|
lAttrValue := ExtractFilePath(FFilename) + lAttrValue;
|
|
|
|
if TvHTMLVectorialReader.IsSupportedRasterImage(lAttrValue) then
|
|
begin
|
|
if not FileExists(lAttrValue) then Continue;
|
|
|
|
lRasterImage := ADest.AddRasterImage();
|
|
lRasterImage.CreateImageFromFile(lAttrValue);
|
|
end
|
|
else if TvVectorialDocument.GetFormatFromExtension(lAttrValue, False) <> vfUnknown then
|
|
begin
|
|
lEmbVecImg := ADest.AddEmbeddedVectorialDoc();
|
|
lEmbVecImg.Document.ReadFromFile(lAttrValue);
|
|
end;
|
|
end;
|
|
'xlink:href':
|
|
begin
|
|
lRasterImage := ADest.AddRasterImage();
|
|
lImageDataParts := TvSVGVectorialReader.ReadSpaceSeparatedStrings(lNodeValue, ':;,');
|
|
try
|
|
if (lImageDataParts.Strings[0] = 'data') and
|
|
(lImageDataParts.Strings[1] = 'image/png') and
|
|
(lImageDataParts.Strings[2] = 'base64') then
|
|
begin
|
|
lImageReader := TFPReaderPNG.Create;
|
|
lImageDataStream := TMemoryStream.Create;
|
|
try
|
|
lImageDataBase64 := lImageDataParts.Strings[3];
|
|
DecodeBase64(lImageDataBase64, lImageDataStream);
|
|
lImageDataStream.Position := 0;
|
|
lRasterImage.CreateRGB888Image(10, 10);
|
|
lRasterImage.RasterImage.LoadFromStream(lImageDataStream, lImageReader);
|
|
finally
|
|
lImageDataStream.Free;
|
|
lImageReader.Free;
|
|
end;
|
|
end
|
|
else
|
|
raise Exception.Create('[TvSVGVectorialReader.ReadImageFromNode] Unimplemented image format');
|
|
finally
|
|
lImageDataParts.Free;
|
|
end;
|
|
end;
|
|
'width':
|
|
begin
|
|
lWidth := StrToInt(lAttrValue);
|
|
end;
|
|
'height':
|
|
begin
|
|
lHeight := StrToInt(lAttrValue);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if lRasterImage <> nil then
|
|
begin
|
|
if lWidth <= 0 then
|
|
lWidth := lRasterImage.RasterImage.Width;
|
|
if lHeight <= 0 then
|
|
lHeight := lRasterImage.RasterImage.Height;
|
|
|
|
lRasterImage.Width := lWidth;
|
|
lRasterImage.Height := lHeight;
|
|
lRasterImage.AltText := lAltText;
|
|
end
|
|
else if (lEmbVecImg <> nil) and (lWidth > 0) and (lHeight > 0) then
|
|
begin
|
|
lEmbVecImg.Width := lWidth;
|
|
lEmbVecImg.Height := lHeight;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
lCurNode := lCurNode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadSVGFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
CurSVG: TvEmbeddedVectorialDoc;
|
|
lText: TvText;
|
|
lDoc: TXMLDocument;
|
|
lImportedNode: TDOMNode;
|
|
begin
|
|
Result := nil;
|
|
CurSVG := AData.AddEmbeddedVectorialDoc();
|
|
lDoc := TXMLDocument.Create;
|
|
try
|
|
lImportedNode := lDoc.ImportNode(ANode, True);
|
|
lDoc.AppendChild(lImportedNode);
|
|
CurSVG.Document.ReadFromXML(lDoc, vfSVG);
|
|
finally
|
|
lDoc.Free;
|
|
end;
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadMathFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
CurSVG: TvEmbeddedVectorialDoc;
|
|
lText: TvText;
|
|
lDoc: TXMLDocument;
|
|
lImportedNode: TDOMNode;
|
|
begin
|
|
Result := nil;
|
|
CurSVG := AData.AddEmbeddedVectorialDoc();
|
|
lDoc := TXMLDocument.Create;
|
|
try
|
|
lImportedNode := lDoc.ImportNode(ANode, True);
|
|
lDoc.AppendChild(lImportedNode);
|
|
CurSVG.Document.ReadFromXML(lDoc, vfMathML);
|
|
finally
|
|
lDoc.Free;
|
|
end;
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadTableFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
CurTable: TvTable;
|
|
lCurNode, lCurSubnode: TDOMNode;
|
|
lNodeName, lNodeValue: DOMString;
|
|
CurRow: TvTableRow;
|
|
Caption_Cell: TvTableCell;
|
|
CurCellPara: TvParagraph;
|
|
// attributes
|
|
i, lBorderNr: Integer;
|
|
lAttrName, lAttrValue: DOMString;
|
|
|
|
procedure SetBorderLineType(AType: TvTableBorderType);
|
|
begin
|
|
CurTable.Borders.Left.LineType := AType;
|
|
CurTable.Borders.Right.LineType := AType;
|
|
CurTable.Borders.Top.LineType := AType;
|
|
CurTable.Borders.Bottom.LineType := AType;
|
|
CurTable.Borders.InsideHoriz.LineType := AType;
|
|
CurTable.Borders.InsideVert.LineType := AType;
|
|
end;
|
|
|
|
begin
|
|
Result := nil;
|
|
CurTable := AData.AddTable();
|
|
CurTable.CellSpacingLeft := 3;
|
|
CurTable.CellSpacingTop := 2;
|
|
|
|
// Default to no border without "border" attribute
|
|
SetBorderLineType(tbtNone);
|
|
|
|
// table attributes
|
|
for i := 0 to ANode.Attributes.Length - 1 do
|
|
begin
|
|
lAttrName := ANode.Attributes.Item[i].NodeName;
|
|
lAttrValue := ANode.Attributes.Item[i].NodeValue;
|
|
|
|
case lAttrName of
|
|
'border':
|
|
begin
|
|
lBorderNr := StrToInt(lAttrValue);
|
|
|
|
SetBorderLineType(tbtSingle);
|
|
CurTable.Borders.Left.Width := lBorderNr;
|
|
CurTable.Borders.Right.Width := lBorderNr;
|
|
CurTable.Borders.Top.Width := lBorderNr;
|
|
CurTable.Borders.Bottom.Width := lBorderNr;
|
|
CurTable.Borders.InsideHoriz.Width := lBorderNr;
|
|
CurTable.Borders.InsideVert.Width := lBorderNr;
|
|
|
|
if lBorderNr = 0 then
|
|
SetBorderLineType(tbtNone);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// table child nodes
|
|
lCurNode := ANode.FirstChild;
|
|
while Assigned(lCurNode) do
|
|
begin
|
|
lNodeName := lCurNode.NodeName;
|
|
lNodeValue := lCurNode.NodeValue;
|
|
case lNodeName of
|
|
'caption':
|
|
begin
|
|
CurRow := CurTable.AddRow();
|
|
Caption_Cell := CurRow.AddCell();
|
|
{Caption_Cell.Borders.Left.LineType := tbtNone;
|
|
Caption_Cell.Borders.Top.LineType := tbtNone;
|
|
Caption_Cell.Borders.Right.LineType := tbtNone;
|
|
Caption_Cell.Borders.Bottom.LineType := tbtNone;}
|
|
CurCellPara := Caption_Cell.AddParagraph();
|
|
CurCellPara.Style := ADoc.StyleTextBodyCentralized;
|
|
CurCellPara.AddText(GetTextContentFromNode(lCurNode));
|
|
end;
|
|
'tbody':
|
|
begin
|
|
lCurSubnode := lCurNode.FirstChild;
|
|
while Assigned(lCurSubnode) do
|
|
begin
|
|
ReadTableRowNode(CurTable, lCurSubnode, AData, ADoc);
|
|
|
|
lCurSubnode := lCurSubnode.NextSibling;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
lCurNode := lCurNode.NextSibling;
|
|
end;
|
|
|
|
// the caption spans all columns
|
|
Caption_Cell.SpannedCols := CurTable.GetColCount();
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadTableRowNode(ATable: TvTable; ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
lCurNode: TDOMNode;
|
|
lNodeName, lNodeValue: DOMString;
|
|
CurRow: TvTableRow;
|
|
CurCell: TvTableCell;
|
|
CurCellPara: TvParagraph;
|
|
begin
|
|
Result := nil;
|
|
CurRow := ATable.AddRow();
|
|
|
|
lCurNode := ANode.FirstChild;
|
|
while Assigned(lCurNode) do
|
|
begin
|
|
lNodeName := lCurNode.NodeName;
|
|
lNodeValue := lCurNode.NodeValue;
|
|
case lNodeName of
|
|
'th':
|
|
begin
|
|
CurCell := CurRow.AddCell();
|
|
CurCellPara := CurCell.AddParagraph();
|
|
CurCellPara.Style := ADoc.StyleTextSpanBold;
|
|
CurCellPara.AddText(GetTextContentFromNode(lCurNode));
|
|
end;
|
|
'td':
|
|
begin
|
|
CurCell := CurRow.AddCell();
|
|
|
|
CurCellPara := CurCell.AddParagraph();
|
|
Self.ReadParagraphFromNode(CurCellPara, lCurNode, AData, ADoc);
|
|
end;
|
|
end;
|
|
|
|
lCurNode := lCurNode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
function TvHTMLVectorialReader.ReadUListFromNode(ANode: TDOMNode;
|
|
AData: TvTextPageSequence; ADoc: TvVectorialDocument): TvEntity;
|
|
var
|
|
lCurNode: TDOMNode;
|
|
lNodeName, lNodeValue: DOMString;
|
|
lNodeText: string;
|
|
//
|
|
lList: TvList;
|
|
lCurPara: TvParagraph;
|
|
begin
|
|
Result := nil;
|
|
lList := AData.AddList();
|
|
|
|
lCurNode := ANode.FirstChild;
|
|
while Assigned(lCurNode) do
|
|
begin
|
|
lNodeName := lCurNode.NodeName;
|
|
lNodeValue := lCurNode.NodeValue;
|
|
case lNodeName of
|
|
'li':
|
|
begin
|
|
lNodeText := GetTextContentFromNode(lCurNode);
|
|
lCurPara := lList.AddParagraph(lNodeText);
|
|
end;
|
|
end;
|
|
|
|
lCurNode := lCurNode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
constructor TvHTMLVectorialReader.Create;
|
|
begin
|
|
inherited Create;
|
|
|
|
FPointSeparator := DefaultFormatSettings;
|
|
FPointSeparator.DecimalSeparator := '.';
|
|
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
|
|
end;
|
|
|
|
destructor TvHTMLVectorialReader.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TvHTMLVectorialReader.ReadFromStrings(AStrings: TStrings;
|
|
AData: TvVectorialDocument);
|
|
var
|
|
Doc: TXMLDocument = nil;
|
|
lStream: TMemoryStream;
|
|
lTmp: String;
|
|
begin
|
|
lStream := TMemoryStream.Create();
|
|
try
|
|
// Remove the <!DOCTYPE line
|
|
if Pos('<!DOCTYPE', AStrings.Strings[0]) <> 0 then
|
|
AStrings.Delete(0);
|
|
// Create a header
|
|
AStrings.Insert(0, '<?xml version="1.0"?>');
|
|
lTmp := AStrings.Text;
|
|
// Flush it back to a stream
|
|
AStrings.SaveToStream(lStream);
|
|
lStream.Position := 0;
|
|
// HTML is not XML, but might be compatible enough... a dedicated reader will be complex, but eventually necessary
|
|
ReadXMLFile(Doc, lStream);
|
|
lStream.Free; // Release as soon as unnecessary
|
|
lStream := nil;
|
|
//
|
|
ReadFromXML(Doc, AData);
|
|
finally
|
|
Doc.Free;
|
|
lStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TvHTMLVectorialReader.ReadFromXML(Doc: TXMLDocument;
|
|
AData: TvVectorialDocument);
|
|
var
|
|
lCurNode, lCurSubnode: TDOMNode;
|
|
lPage: TvTextPageSequence;
|
|
lNodeName, lNodeValue: DOMString;
|
|
ANode: TDOMElement;
|
|
i: Integer;
|
|
lCurEntity: TvEntity;
|
|
begin
|
|
{ANode := Doc.DocumentElement;
|
|
for i := 0 to ANode.Attributes.Length - 1 do
|
|
begin
|
|
lNodeName := ANode.Attributes.Item[i].NodeName;
|
|
lNodeValue := ANode.Attributes.Item[i].NodeValue;
|
|
end;}
|
|
|
|
AData.AddStandardTextDocumentStyles(vfHTML);
|
|
|
|
// ----------------
|
|
// Now process the elements
|
|
// ----------------
|
|
lCurNode := Doc.DocumentElement.FirstChild;
|
|
lPage := AData.AddTextPageSequence();
|
|
//lPage.Width := AData.Width;
|
|
//lPage.Height := AData.Height;
|
|
while Assigned(lCurNode) do
|
|
begin
|
|
lNodeName := lCurNode.NodeName;
|
|
if lNodeName = 'body' then
|
|
begin
|
|
lCurSubnode := lCurNode.FirstChild;
|
|
while Assigned(lCurSubnode) do
|
|
begin
|
|
lCurEntity := ReadEntityFromNode(lCurSubnode, lPage, AData);
|
|
if lCurEntity <> nil then
|
|
lPage.AddEntity(lCurEntity);
|
|
|
|
lCurSubnode := lCurSubnode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
lCurNode := lCurNode.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
class function TvHTMLVectorialReader.IsSupportedRasterImage(AFileName: string): Boolean;
|
|
var
|
|
lExt: string;
|
|
begin
|
|
Result := False;
|
|
lExt := LowerCase(ExtractFileExt(AFileName));
|
|
case lExt of
|
|
'.png', '.jpg', '.jpeg', '.bmp', '.xpm':
|
|
Result := True
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
|
|
RegisterVectorialReader(TvHTMLVectorialReader, vfHTML);
|
|
|
|
end.
|
|
|