{************************************************************}
{                   Delphi VCL Extensions                    }
{                                                            }
{                  DWF files TGraphic class                  }
{                                                            }
{      Copyright (c) 2002-2013 SoftGold software company     }
{                                                            }
{************************************************************}

unit DWF;
{$INCLUDE SGDXF.INC}

interface

uses
  SysUtils, Classes, Windows, Graphics, sgConsts, DXFConv, CADImage, W2D, sgZIP;

const
  SDWFImage = 'DWF image';

var
  CF_DWF: Word;

type
{ TsgDWFImage
  Top-level class. }
  TsgDWFImage = class(TsgVectorImage)
  private
    FCoef: Double;
    FPercent: Integer;
    FSize: Integer;
    procedure ParserProgress(Sender: TObject; Percent: Double);
    procedure ParseAndLoadToStream(const AW2DParser: TwxdParser;
      const ALayout: TsgDXFLayout; const AStream: TMemoryStream);
  protected
    procedure ApplyLineWeightFactor(var ALineWeight: Double); override;
  public
    procedure LoadFromStream(AStream: TStream); override;
  end;

implementation

type
  TsgDXFGroupAccess = class(TsgDXFGroup);
  TdwfConverter = class(TsgDXFConverter);

function IsOldDWF(const AStream: TStream): Boolean;
var
  vS1, vS2: AnsiString;
  vDS: Char;
begin
  Result := False;
  SetLength(vS1, 6);
  SetLength(vS2, 5);
  AStream.Read(PAnsiChar(vS1)^, Length(vS1));
  AStream.Read(PAnsiChar(vS2)^, Length(vS2));
  AStream.Position := AStream.Position - 11;
  if vS1 = '(DWF V' then
  begin
    vDS := SetDecimalSeparator('.');
    try
      try
        Result := StrToFloat(string(vS2)) <= 0.55
      except
      end;
    finally
      SetDecimalSeparator(vDS);
    end;
  end;
end;

{ TsgDWFImage }

procedure TsgDWFImage.ParserProgress(Sender: TObject; Percent: Double);
begin
  Progress(Self, psRunning, Round(FPercent + Percent * FCoef), False,
    sgEmptyProgressRect, '');
end;

procedure TsgDWFImage.ApplyLineWeightFactor(var ALineWeight: Double);
begin
  ALineWeight := ALineWeight * 0.25;
end;

procedure TsgDWFImage.ParseAndLoadToStream(const AW2DParser: TwxdParser;
    const ALayout: TsgDXFLayout; const AStream: TMemoryStream);
var
  I: Integer;
  vEntity : TsgDXFEntity;
begin
  AW2DParser.OnProgress := ParserProgress;
  Converter.Loads(ALayout);//by set converter
  AW2DParser.LoadFromStream(Converter, AStream);
  for I := 0 to AW2DParser.Entities.Count - 1 do
  begin
    vEntity:= AW2DParser.Entities[I];
    ALayout.AddEntity(vEntity);
    if Assigned(Converter.OnCreate) then
      Converter.OnCreate(vEntity);
    Converter.Loads(vEntity);
  end;
  Converter.Loads(ALayout);
end;

procedure TsgDWFImage.LoadFromStream(AStream: TStream);
const
  cnstNoFiles = 'There are no W2D files in this DWF package';
var
  I: Integer;
  vW2DParser: TwxdParser;
  vDWFUnzip: TsgDWFUnzip;
  vStream: TMemoryStream;
  vPlotSection: TsgEPlotSection;
  vLayoutName: string;
  vLayout: TsgDXFLayout;

  procedure Extract(const ADWFUnzip: TsgDWFUnzip;
    const APlotSection: TsgEPlotSection; const AStream: TStream);
  begin
    Inc(FSize, ADWFUnzip.CurrentSize);
    if not ADWFUnzip.ExtractFile(APlotSection.IndInZip, AStream) then
      raise EInvalidGraphic.Create('Invalid DWF data');
    FPercent := Round(100 * FSize / ADWFUnzip.TotalSize);
    FCoef := ADWFUnzip.CurrentSize / ADWFUnzip.TotalSize;
  end;
begin
  inherited LoadFromStream(AStream);
  vStream := nil;

  Converter.InitializeSectionsBegin;

  vDWFUnzip := TsgDWFUnzip.Create;
  try
    Progress(Self, psStarting, 0, False, sgEmptyProgressRect, '');
    vStream := TMemoryStream.Create;
    vLayoutName := '';
    FCoef := 1.0;
    if IsOldDWF(AStream) then
      vStream.LoadFromStream(AStream)
    else
    begin
      vDWFUnzip.LoadFromStream(AStream);
      if vDWFUnzip.EPlotSections.Count = 0 then
      begin
        GetExtents;
        raise EInvalidGraphic.Create(cnstNoFiles);
      end;
    end;
//    BackgroundColor := vW2DParser.BackgroundColor;
    while Converter.LayoutsCount > 1 do
    begin
      vLayout := Converter.Layouts[Converter.LayoutsCount - 1];
      Converter.DeleteLayout(vLayout);
      vLayout.Free;
    end;
    if vDWFUnzip.EPlotSections.Count = 0 then
    begin
      vW2DParser := Tw2dParser.Create;
      try
        vLayout := Converter.Layouts[0];
        vLayout.CADSpace := cs2D;
        ParseAndLoadToStream(vW2DParser, vLayout, vStream);
      finally
        vW2DParser.Free;
      end;
    end
    else
      for I := 0 to vDWFUnzip.EPlotSections.Count - 1 do
      begin
        vPlotSection := TsgEPlotSection(vDWFUnzip.EPlotSections[I]^);
        vStream.Clear;
        Extract(vDWFUnzip, vPlotSection, vStream);
        if vPlotSection.LayoutIndex >= 0 then
          vLayoutName := vDWFUnzip.Layouts[vPlotSection.LayoutIndex]
        else
          vLayoutName := ExtractFileName(vPlotSection.Graphic);
        if Converter.LayoutByName(vLayoutName) <> nil then
          vLayoutName := vLayoutName + '_' + IntToStr(I);
        if (vDWFUnzip.EPlotSections.Count = 0) or (vPlotSection.FileExt = feW2D) then
          vW2DParser := Tw2dParser.Create
        else
          vW2DParser := Tw3dParser.Create;
        try
          if I < Converter.LayoutsCount then
            vLayout := Converter.Layouts[I]
          else
            vLayout := TsgDXFLayout(Converter.NewNamedEntity(Converter.Sections[csLayouts], TsgDXFLayout, vLayoutName));
          if vPlotSection.FileExt = feW3D then
            vLayout.CADSpace := cs3D
          else
            vLayout.CADSpace := cs2D;
          ParseAndLoadToStream(vW2DParser, vLayout, vStream);
        finally
          vW2DParser.Free;
        end;
      end;
    SetDefaultViewPort(Converter);
    GetExtents;
    SetDefaultPlotSettings;
  finally
    vStream.Free;
    vDWFUnzip.Free;
    Progress(Self, psEnding, 100, False, sgEmptyProgressRect, '');
  end;
end;

initialization
  CF_DWF := RegisterClipboardFormat('DWF Image');
  TPicture.RegisterClipboardFormat(CF_DWF, TsgDWFImage);
  TPicture.RegisterFileFormat('DWF', SDWFImage, TsgDWFImage);
  TPicture.RegisterFileFormat('DWFX', SDWFImage, TsgDWFImage);

finalization
  TPicture.UnRegisterGraphicClass(TsgDWFImage);

end.
