unit LoadDMF;

//фрагмент кода, демонстрирующий загрузку DMF файлов версии 1.10
//поддерживается компрессия, запароленные файлы не читаются
//(c) Geosystem 2007 http://www.vingeo.com
//Bondarets Alexander bond@vingeo.com

interface

implementation

//******************************************************************************
//DMF types
type

  TDMF3D=packed record
    X,Y,Z:Extended;
  end;

  TDMF3DFrame=array[0..3] of TDMF3D;

  TDMFSignature=array[1..32] of char;

  TDMFMapHeader=packed record
     Scale:Extended;
     Count:integer;
     Units:integer;
     Status:integer;
     Frame:TDMF3DFrame;
     Name:ShortString;
     LeftFile:ShortString;
     RightFile:ShortString;
  end;

  TDMFLayerHeader=packed record
    Size:integer;
    Status:integer;
    Count:integer;
    MinService:integer;
    Reserve:Byte;
    Empty:integer;
  end;

  TDMFLayer1=packed record
    Size:integer;
    Status:integer;
    ID:integer;
    MinScale:integer;
    MaxScale:integer;
    PenColor:integer;
    PenWidth:integer;
    BrushColor:integer;
    FontColor:integer;
    FontSize:integer;
    PenStyle:Byte;
    BrushStyle:Byte;
    FontStyle:Byte;
  end;
  TDMFLayer2=packed record
    Name:string;
    FontName:string;
    BrushBMPSize:integer;
    ParamLength:integer;
    Params:TByteArray;
    Symbol:integer;
    Format:string;
    Reference:integer;
    PenWidth100:integer;
    FontSize10:integer;
  end;

  TDMFParamHeader=packed record
    Size:integer;
    Status:integer;
    Count:integer;
    MinService:integer;
    Reserve:Byte;
    Empty:integer;
  end;

  TDMFParam1=packed record
    Size:integer;
    Status:integer;
    ID:integer;
    MinScale:integer;
    MaxScale:integer;
    PenColor:integer;
    PenWidth:integer;
    BrushColor:integer;
    FontColor:integer;
    FontSize:integer;
    PenStyle:Byte;
    BrushStyle:Byte;
    FontStyle:Byte;
  end;
  TDMFParam2=packed record
    Name:string;
    FontName:string;
    BrushBMPSize:integer;
    ParamLength:integer;
    Params:TByteArray;
    Symbol:integer;
    Format:string;
    Reference:integer;
    PenWidth100:integer;
    FontSize10:integer;
  end;

  TDMFSymbolHeader=packed record
    Size:integer;
    Count:integer;
  end;

  TDMFSymbol=packed record
    Size:integer;
    HeaderSize:integer;
    ID:integer;
    Count:integer;
    Length:integer;
    Kind:integer;
    Height:integer;
  end;

  TDMFSymbolPrimitive=packed record
    Kind:Char;
    Group:Byte;
    PenStyle:Byte;
    BrushStyle:Byte;
    PenColor:integer;
    PenWidth:integer;
    BrushColor:integer;
    X1:integer;
    Y1:integer;
    X2:integer;
    Y2:integer;
  end;

  TDMFObject1=packed record
    Size:integer;
    Format:Word;
    HeaderSize:integer;
    Count:integer;
    LayerID:integer;
    Tag:integer;
    Layer:integer;
    ID:integer;
    Status:integer;
    TS:Cardinal;
    NS:Single;
    Group:integer;
    Parent:integer;
    SO:integer;
  end;

  TDMFObject2=packed record
    ParamSize:integer;
    Params:string;
  end;

  TDMFObjectPoint=packed record
    Status:integer;
    X:Extended;
    Y:Extended;
    Z:Extended;
  end;

  TDMFImageOLE=packed record
    Typ,Size:integer;
  end;

const CDMFSignature='DMF';
      CDMFVer10='1.10';
      CDMFVer15='1.15';
      //выравнивание подписей
      CDMFLblLeft=0;
      CDMFLblMiddle=1;
      CDMFLblRight=2;
      CDMFLblTop=0;
      CDMFLblCenter=1;
      CDMFLblBottom=2;
      CDMFLblNone=129; //эти подписи не отображаются
      //статус слоя и параметра
      CDMFStEdit=0;
      CDMFStSelect=1;
      CDMFStVisible=2;
      CDMFStHide=3;
      //коды строки параметров
      CDMFParStart=#1;
      CDMFParEnd=#2;
      CDMFParSep=#3;
      CDMFParInfo=#5;
      CDMFParMultiSep='|';
      CDMFTblHdrEnd=#7;
      CDMFTblColEnd=#9;
      CDMFTblRowEnd=#10;
      CDMFTblInfSep=' ';
      CDMFTblLineSep='||';
      CDMFTblCols='COLS';
      CDMFTblRows='ROWS';
      CDMFTblLAlign='L';
      CDMFTblCAlign='C';
      CDMFTblRAlign='R';
      CDMFTblRowHeight='ROWHEIGHT';
      CDMFTblFormat='FORMAT';
      CDMFTblNoHeader='NOHEADER';
      CDMFPrimDefs:array[TPrimitiveKind] of Char=('P','C','R','M',#0);
      CDMFAlignDefs:array[THorizAlign] of Char=('L','C','R');
      CDMFHAlignCnts:array[THorizAlign] of integer=(CDMFLblLeft,CDMFLblMiddle,CDMFLblRight);
      CDMFVAlignCnts:array[TVertAlign] of integer=(CDMFLblTop,CDMFLblCenter,CDMFLblBottom);

//******************************************************************************
procedure TVectorMap.LoadDMF(FileName:string);
//порядок описания заголовка таблицы
const CTblHdrDef:array[0..4] of string=(CDMFTblCols,CDMFTblRowHeight,CDMFTblFormat,
                                        CDMFTblRows,CDMFTblNoHeader);
var F,S:TStream;
    VDbl:Double;
    StrSize:Byte;
    HdrSize,DMFVer,Ind,LrInd,ParInd,PrimInd,ByteInd,Mask,
    ObjInd,PntInd,StartPos,EndPos,VInt,N,CurInd,BitInd,
    InfInd,InfLen,FndInd,EndInd,EndInd2,EndList,HdrEnd,
    FormSize,HdrInd,CCnt,RCnt:integer;
    S0,S1:string;
    I,OldDecSep:Char;
    R:T2DRect; Al:THorizAlign;
    KindInd:TPrimitiveKind;
    DMFSign:TDMFSignature;
    DMFMapHdr:TDMFMapHeader;
    DMFLayerHdr:TDMFLayerHeader;
    DMFLayer1:TDMFLayer1;
    DMFLayer2:TDMFLayer2;
    DMFParamHdr:TDMFParamHeader;
    DMFParam1:TDMFParam1;
    DMFParam2:TDMFParam2;
    DMFSymbHdr:TDMFSymbolHeader;
    DMFSymb:TDMFSymbol;
    DMFSymbPrim:TDMFSymbolPrimitive;
    DMFObj1:TDMFObject1;
    DMFObj2:TDMFObject2;
    DMFObjPnt:TDMFObjectPoint;
    DMFImgOLE:TDMFImageOLE;
    Layer:TMapLayer;
    Param:TMapParam;
    Symb:TMapSymbol;
    Prim:TSymbPrimitive;
    Obj:TMapObject;
    Lbl:TMapLabel;
    Tbl:TMapTable;
begin

 F:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);

 OldDecSep:=DecimalSeparator;
 DecimalSeparator:='.';

 //считываем сначала в DMF структуры для
 //уменьшения кол-ва обращений к файлу
 try

  Clear;

  //сигнатура DMF
  if F.Read(DMFSign,SizeOf(DMFSign))<>SizeOf(DMFSign)
   then Error(SFileCorrupted);

  //проверяем сигнатуру
  if Pos(CDMFSignature,UpperCase(DMFSign))=0
   then Error(SNoDMFFile);

  //определяем версию DMF
  DMFVer:=0;
  if Pos(CDMFVer10,DMFSign)>0
   then DMFVer:=110
   else
  if Pos(CDMFVer15,DMFSign)>0
   then DMFVer:=115;

  //если имеется признак сжатого
  //файла -создаем поток для декомпрессии
  if UpCase(DMFSign[29])='C'
   then S:=TDecompressionStream.Create(F)
   else S:=F;

  //заголовок карты
  S.Read(HdrSize,SizeOf(HdrSize));
  if S.Read(DMFMapHdr,HdrSize)<>HdrSize
   then Error(SFileCorrupted);

  FScale:=DMFMapHdr.Scale;

  //заголовок списка слоев
  if S.Read(DMFLayerHdr,SizeOf(DMFLayerHdr))<>SizeOf(DMFLayerHdr)
   then Error(SFileCorrupted);

  EndList:=S.Position-SizeOf(DMFLayerHdr)+
           SizeOf(DMFLayerHdr.Size)+DMFLayerHdr.Size;

  //проверяем или не слишком много слоев в списке и
  //не выходит ли размер списка слоев за размер файла
  if (DMFLayerHdr.Count>High(Word)) or
     (DMFLayerHdr.Count<0) or
     (DMFLayerHdr.Size<0) then Error(SFileCorrupted);

  //индекс первого слоя
  DMFLayerHdr.MinService:=GetMinLayer;

  LrInd:=1;
  //считываем список слоев
  while S.Position<EndList do begin

   //начальная позиция элемента
   StartPos:=S.Position+SizeOf(DMFLayerHdr.Size);
   S.Read(DMFLayer1,SizeOf(DMFLayer1));
   //конечная позиция элемента
   EndPos:=StartPos+DMFLayer1.Size;
   //создаем новый слой
   Layer:=TMapLayer.Create(Self);

   //название слоя
   S.Read(StrSize,SizeOf(StrSize));
   SetLength(DMFLayer2.Name,StrSize);
   if StrSize>0 then S.Read(DMFLayer2.Name[1],StrSize);
   //имя шрифта
   S.Read(StrSize,SizeOf(StrSize));
   SetLength(DMFLayer2.FontName,StrSize);
   if StrSize>0 then S.Read(DMFLayer2.FontName[1],StrSize);
   //BrushBMPSize
   S.Read(DMFLayer2.BrushBMPSize,SizeOf(DMFLayer2.BrushBMPSize));
   if DMFLayer2.BrushBMPSize>0 then begin
    //BrushPattern
    Layer.FBrushPattern:=TDIB.Create;
    Layer.FBrushPattern.LoadFromStream(S);
   end;
   //длина битового массива параметров слоя
   S.Read(DMFLayer2.ParamLength,SizeOf(DMFLayer2.ParamLength));
   SetLength(DMFLayer2.Params,DMFLayer2.ParamLength);
   //битовый массив параметров
   if DMFLayer2.ParamLength>0 then S.Read(DMFLayer2.Params[0],DMFLayer2.ParamLength);
   //номер условного знака слоя
   S.Read(DMFLayer2.Symbol,SizeOf(DMFLayer2.Symbol));
   //строка формата слоя
   S.Read(StrSize,SizeOf(StrSize));
   if StrSize=255
    then S.Read(FormSize,SizeOf(FormSize))
    else FormSize:=StrSize;
   SetLength(DMFLayer2.Format,FormSize);
   if FormSize>0 then S.Read(DMFLayer2.Format[1],FormSize);

   //эти поля были добавлены в DMF позднее
   DMFLayer2.Reference:=0;
   DMFLayer2.PenWidth100:=0;
   DMFLayer2.FontSize10:=0;
   if S.Position<EndPos then begin
    //Reference
    S.Read(DMFLayer2.Reference,SizeOf(DMFLayer2.Reference));
    //PenWidth100
    S.Read(DMFLayer2.PenWidth100,SizeOf(DMFLayer2.PenWidth100));
    //FontSize10
    S.Read(DMFLayer2.FontSize10,SizeOf(DMFLayer2.FontSize10));
   end;

   Layer.FName:=DMFLayer2.Name;
   Layer.FID:=DMFLayer1.ID;
   //статус слоя
   Layer.FStatus:=TLayerStatus(Lo(HiWord(DMFLayer1.Status)));
   //тип слоя
   Layer.FTyp:=TLayerType(Hi(HiWord(DMFLayer1.Status)));
   //вид слоя
   if LrInd<=byte(High(TLayerKind))
    then Layer.FKind:=TLayerKind(LrInd);
   //локализация слоя
   Layer.FLocale:=TLayerLocale(Lo(LoWord(DMFLayer1.Status)) and 1);
   //строка формата
   Layer.FFormat:=DMFLayer2.Format;
   //масштабы отображения
   Layer.FMinScale:=DMFLayer1.MinScale;
   Layer.FMaxScale:=DMFLayer1.MaxScale;
   //кисть, перо, шрифт
   Layer.FPenWidth:=DMFLayer1.PenWidth+DMFLayer2.PenWidth100*0.1;
   Layer.FPenColor:=DMFLayer1.PenColor;
   Layer.FPenStyle:=TPenStyle(DMFLayer1.PenStyle);
   Layer.FBrushColor:=DMFLayer1.BrushColor;
   Layer.FBrushStyle:=TBrushStyle(DMFLayer1.BrushStyle);
   //за именем шрифта, отделенный двоеточием, может
   //следовать номер CharSet, нам это значение не нужно
   StrSize:=Pos(':',DMFLayer2.FontName);
   if StrSize=0
    then Layer.FFontName:=DMFLayer2.FontName
    else Layer.FFontName:=Copy(DMFLayer2.FontName,1,StrSize-1);
   Layer.FFontColor:=DMFLayer1.FontColor;
   Layer.FFontSize:=DMFLayer1.FontSize+DMFLayer2.FontSize10*0.1;
   Layer.FFontStyles:=TFontStyles(DMFLayer1.FontStyle);

   //доступные слою параметры
   //сначала записываем в список индексы, на
   //их место запишем ссылки на объекты
   ParInd:=0;
   for ByteInd:=0 to High(DMFLayer2.Params) do begin
    //проходим по байтам
    Mask:=1;
    for BitInd:=1 to 8 do begin
     //внутри каждого байта -по битам
     if (Mask and DMFLayer2.Params[ByteInd])<>0
        then Layer.FAvailableParams.Add(Pointer(ParInd));
     Inc(ParInd);
     Mask:=Mask shl 1;
    end;
   end;

   //индекс символа в массиве символов
   Layer.FSymbol:=Pointer(DMFLayer2.Symbol);
   //добавляем слой
   FLayers.Add(Layer);
   Inc(LrInd);

   if EndPos>S.Position
    //считали меньше данных, чем требуется
    //устанавливаем позицию -начало след. слоя
    then S.Seek(EndPos,soFromBeginning)
    //если считали больше данных, чем требуется -ошибка
    else if EndPos<S.Position
     then Error(SFileCorrupted);

  end;

  //заголовок списка параметров
  if S.Read(DMFParamHdr,SizeOf(DMFParamHdr))<>SizeOf(DMFParamHdr)
   then Error(SFileCorrupted);

  EndList:=S.Position-SizeOf(DMFParamHdr)+
           SizeOf(DMFParamHdr.Size)+DMFParamHdr.Size;

  //проверяем или не слишком много параметров в списке и
  //не выходит ли размер списка параметров за размер файла
  if (DMFParamHdr.Count>High(Word)) or
     (DMFParamHdr.Count<0) or
     (DMFParamHdr.Size<0) then Error(SFileCorrupted);

  //индекс первого параметра
  DMFParamHdr.MinService:=GetMinParam;

  ParInd:=1;
  //считываем список параметров
  while S.Position<EndList do begin

   //начальная позиция элемента
   StartPos:=S.Position+SizeOf(DMFParamHdr.Size);
   S.Read(DMFParam1,SizeOf(DMFParam1));
   //конечная позиция элемента
   EndPos:=StartPos+DMFParam1.Size;
   //создаем новый параметр
   Param:=TMapParam.Create(Self);

   //название параметра
   S.Read(StrSize,SizeOf(StrSize));
   SetLength(DMFParam2.Name,StrSize);
   if StrSize>0 then S.Read(DMFParam2.Name[1],StrSize);
   //имя шрифта
   S.Read(StrSize,SizeOf(StrSize));
   SetLength(DMFParam2.FontName,StrSize);
   if StrSize>0 then S.Read(DMFParam2.FontName[1],StrSize);
   //BrushBMPSize
   S.Read(DMFParam2.BrushBMPSize,SizeOf(DMFParam2.BrushBMPSize));
   if DMFParam2.BrushBMPSize>0 then begin
    //BrushPattern
    Param.FBrushPattern:=TDIB.Create;
    Param.FBrushPattern.LoadFromStream(S);
   end;
   //длина битового массива параметров
   S.Read(DMFParam2.ParamLength,SizeOf(DMFParam2.ParamLength));
   SetLength(DMFParam2.Params,DMFParam2.ParamLength);
   //битовый массив параметров (не используется)
   if StrSize>0 then S.Read(DMFParam2.Params[0],DMFParam2.ParamLength);
   //номер условного знака параметра
   S.Read(DMFParam2.Symbol,SizeOf(DMFParam2.Symbol));
   //строка формата параметра
   S.Read(StrSize,SizeOf(StrSize));
   //вот она непредусмотрительность разработчиков
   //со временем понадобилось хранить в формате длинные
   //списки, а длина строки ограничена 255 символами, для длинных
   //строк после StrSize снова пишется длина строки (тип integer)
   if StrSize=255
    then S.Read(FormSize,SizeOf(FormSize))
    else FormSize:=StrSize;
   SetLength(DMFParam2.Format,FormSize);
   if FormSize>0 then S.Read(DMFParam2.Format[1],FormSize);

   //эти поля были добавлены в DMF позднее
   DMFParam2.Reference:=0;
   DMFParam2.PenWidth100:=0;
   DMFParam2.FontSize10:=0;
   if S.Position<EndPos then begin
    //Reference
    S.Read(DMFParam2.Reference,SizeOf(DMFParam2.Reference));
    //PenWidth100
    S.Read(DMFParam2.PenWidth100,SizeOf(DMFParam2.PenWidth100));
    //FontSize10
    S.Read(DMFParam2.FontSize10,SizeOf(DMFParam2.FontSize10));
   end;

   Param.FName:=DMFParam2.Name;
   Param.FID:=DMFParam1.ID;
   //статус параметра
   Param.FStatus:=TParamStatus(Lo(HiWord(DMFParam1.Status)));
   //тип параметра
   Param.FTyp:=TParamType(Hi(HiWord(DMFParam1.Status))-1);
   //строка формата
   Param.FFormat:=DMFParam2.Format;
   //масштабы отображения
   Param.FMinScale:=DMFParam1.MinScale;
   Param.FMaxScale:=DMFParam1.MaxScale;
   //вид параметра
   if ParInd<=byte(High(TParamKind))
    then Param.FKind:=TParamKind(ParInd);
   //перо, кисть, шрифт
   Param.FPenWidth:=DMFParam1.PenWidth+DMFParam2.PenWidth100*0.1;
   Param.FPenColor:=DMFParam1.PenColor;
   Param.FPenStyle:=TPenStyle(DMFParam1.PenStyle);
   Param.FBrushColor:=DMFParam1.BrushColor;
   Param.FBrushStyle:=TBrushStyle(DMFParam1.BrushStyle);
   //за именем шрифта, отделенный двоеточием, может
   //следовать номер CharSet, нам это значение не нужно
   StrSize:=Pos(':',DMFParam2.FontName);
   if StrSize=0
    then Param.FFontName:=DMFParam2.FontName
    else Param.FFontName:=Copy(DMFParam2.FontName,1,StrSize-1);
   Param.FFontColor:=DMFParam1.FontColor;
   Param.FFontSize:=DMFParam1.FontSize+DMFParam2.FontSize10*0.1;
   Param.FFontStyles:=TFontStyles(DMFParam1.FontStyle);

   //индекс символа в массиве символов
   Param.FSymbol:=Pointer(DMFParam2.Symbol);

   //добавляем параметр
   FParams.Add(Param);
   Inc(ParInd);

   if EndPos>S.Position
    //считали меньше данных, чем требуется
    //устанавливаем позицию -начало след. параметра
    then S.Seek(EndPos,soFromBeginning)
    //если считали больше данных, чем требуется -ошибка
    else if EndPos<S.Position then Error(SFileCorrupted);

  end;

  //заголовок списка условных знаков
  if S.Read(DMFSymbHdr,SizeOf(DMFSymbHdr))<>SizeOf(DMFSymbHdr)
   then Error(SFileCorrupted);

  EndList:=S.Position-SizeOf(DMFSymbHdr)+
           SizeOf(DMFSymbHdr.Size)+DMFSymbHdr.Size;

  //проверяем или не слишком много символов в списке и
  //не выходит ли размер списка параметров за размер файла
  if (DMFSymbHdr.Count>High(Word)) or
     (DMFSymbHdr.Count<0) or
     (DMFSymbHdr.Size<0) then Error(SFileCorrupted);

  //считываем список символов
  while S.Position<EndList do begin

   //начальная позиция элемента
   StartPos:=S.Position+SizeOf(DMFSymb.Size);
   S.Read(DMFSymb,SizeOf(DMFSymb));
   //конечная позиция элемента
   EndPos:=StartPos+DMFSymb.Size;

   //создаем новый символ
   Symb:=TMapSymbol.Create(Self);
   Symb.FID:=DMFSymb.ID;
   Symb.FKind:=TSymbolKind(DMFSymb.Kind);
   Symb.FWidth:=DMFSymb.Length;
   Symb.FHeight:=DMFSymb.Height;

   //считываем примитивы символа
   for PrimInd:=0 to DMFSymb.Count-1 do begin

    S.Read(DMFSymbPrim,SizeOf(DMFSymbPrim));

    //создаем новый примитив
    Prim:=TSymbPrimitive.Create(Symb);
    //определяем вид примитива
    Prim.FKind:=pkUnknown;
    for KindInd:=Low(CDMFPrimDefs) to High(CDMFPrimDefs) do
     if CDMFPrimDefs[KindInd]=UpCase(DMFSymbPrim.Kind)
      then begin
       Prim.FKind:=KindInd;
       break;
      end;

    Prim.FGroup:=DMFSymbPrim.Group;
    Prim.FPenWidth:=DMFSymbPrim.PenWidth;
    Prim.FPenColor:=DMFSymbPrim.PenColor;
    Prim.FPenStyle:=TPenStyle(DMFSymbPrim.PenStyle);
    Prim.FBrushColor:=DMFSymbPrim.BrushColor;
    Prim.FBrushStyle:=TBrushStyle(DMFSymbPrim.BrushStyle);
    Prim.FX1:=DMFSymbPrim.X1;
    Prim.FY1:=DMFSymbPrim.Y1;
    Prim.FX2:=DMFSymbPrim.X2;
    Prim.FY2:=DMFSymbPrim.Y2;

    //добаляем примитив к списку
    Symb.FPrimitives.Add(Prim);

   end;

   //добавляем символ
   FSymbols.Add(Symb);

   if EndPos>S.Position
    //считали меньше данных, чем требуется
    //устанавливаем позицию -начало след. символа
    then S.Seek(EndPos,soFromBeginning)
    //если считали больше данных, чем требуется -ошибка
    else if EndPos<S.Position then Error(SFileCorrupted);

  end;

  Obj:=nil; ObjInd:=0;
  //считываем список объектов
  while ObjInd<DMFMapHdr.Count do begin

   //начальная позиция элемента
   StartPos:=S.Position+SizeOf(DMFObj1.Size);
   //проверяем целостность файла, в случае ошибки -прекращаем чтение
   if S.Read(DMFObj1,SizeOf(DMFObj1))<>SizeOf(DMFObj1) then break;
   //конечная позиция элемента
   EndPos:=StartPos+DMFObj1.Size;

   //считываем значения параметров объекта
   if S.Read(DMFObj2.ParamSize,SizeOf(DMFObj2.ParamSize))<>
      SizeOf(DMFObj2.ParamSize) then break;
   SetLength(DMFObj2.Params,DMFObj2.ParamSize);
   if S.Read(DMFObj2.Params[1],DMFObj2.ParamSize)<>DMFObj2.ParamSize
    then break;

   //создаем новый объект
   S0:=DMFObj2.Params;
   if (S0<>'') and CompareMem(@S0[1],@CDMFTblCols[1],Length(CDMFTblCols))
    then Obj:=TMapTable.Create(Self)   //объект -таблица
    else Obj:=TMapObject.Create(Self);
   Obj.FID:=DMFObj1.ID;
   Obj.FSymbRotate:=DMFObj1.SO;
   Obj.FScale:=1;
   //масштаб объекта
   if DMFObj1.NS>0 then Obj.FScale:=DMFObj1.NS;
   //назначаем объекту слой
   LrInd:=DMFObj1.Layer-DMFLayerHdr.MinService;
   if LrInd<FLayers.Count then Obj.SetLayer(FLayers[LrInd]);

   CurInd:=1;
   //преобразовываем строку параметров объекта
   //в список подписей или таблицу
   if Obj is TMapTable then begin
    Tbl:=TMapTable(Obj);
    //считываем заголовок таблицы
    if FindSubStr(CDMFTblHdrEnd,S0,CurInd,Length(S0),HdrEnd)
     then begin

      for HdrInd:=0 to High(CTblHdrDef) do
       try
        S1:=CTblHdrDef[HdrInd];
        N:=Length(S1);
        //поиск идентификатора
        if not FindSubStr(S1,S0,CurInd,CurInd+N,FndInd) or
           //поиск завершающего символа параметра
           not FindSubStr([CDMFTblRowEnd,CDMFTblHdrEnd],
                           S0,CurInd,HdrEnd,EndInd) then Continue;
        Inc(CurInd,N+1);
        //разбор параметров, пример строки
        //параметра <10>ROWHEIGHT 10000<10>
        case HdrInd of
         0: //COLS
            begin
             CCnt:=0;
             while FindSubStr([CDMFTblInfSep,CDMFTblRowEnd,CDMFTblHdrEnd],
                              S0,CurInd,EndInd,FndInd) do
              begin
               S1:=Copy(S0,CurInd,FndInd-CurInd);
               if S1='' then Continue;
               if CCnt=0 then begin
                //количество колонок
                if not CheckNumber(S1,VInt,false) then Continue;
                SetLength(Tbl.FCols,VInt);
               end else begin
                if CCnt>Length(Tbl.FCols) then Continue;
                //выравнивание в колонке
                Tbl.FCols[CCnt-1].Align:=haLeft;
                for Al:=Low(THorizAlign) to High(THorizAlign) do
                 if CDMFAlignDefs[Al]=S1[1] then Tbl.FCols[CCnt-1].Align:=Al;
                Delete(S1,1,1);
                Tbl.FCols[CCnt-1].Auto:=true;
                if S1='' then Continue;
                if S1[1]='+' then begin
                 //ширина колонки фиксирована
                 Tbl.FCols[CCnt-1].Auto:=false;
                 Delete(S1,1,1);
                end;
                if not CheckNumber(S1,VInt,false) then Continue;
                Tbl.FCols[CCnt-1].Width:=VInt;
               end;
               CurInd:=FndInd+1;
               Inc(CCnt);
              end;
             end;
         1: //ROWHEIGHT
            begin
             S1:=Copy(S0,CurInd,EndInd-CurInd);
             if not CheckNumber(S1,VInt,false) then Continue;
             Tbl.FRowHeight:=VInt;
            end;
         2: //FORMAT
            begin
             S1:=Copy(S0,CurInd,EndInd-CurInd);
             Tbl.FFormat:=S1;
            end;
         3: //ROWS
            begin
             S1:=Copy(S0,CurInd,EndInd-CurInd);
             if not CheckNumber(S1,VInt,false) then Continue;
             Tbl.FRowCount:=VInt;
            end;
         4: //NOHEADER
            Tbl.FHeader:=false;
        end;
       finally
        //переходим на начало след параметра
        CurInd:=EndInd+1;
       end;
      //считываем строки
      SetLength(Tbl.FCells,Tbl.ColCount,Tbl.RowCount);
      CCnt:=0; RCnt:=0;
      //цикл по строкам
      while FindSubStr(CDMFTblRowEnd,S0,CurInd,Length(S0),EndInd) do
       //цикл по столбцам
       while FindSubStr([CDMFTblColEnd,CDMFTblRowEnd],
                        S0,CurInd,EndInd,FndInd) do
        try
         //текст ячейки
         S1:=Copy(S0,CurInd,FndInd-CurInd);
         //заменяем символ переноса строки на #13#10
         S1:=StringReplace(S1,CDMFTblLineSep,#13#10,[rfReplaceAll]);
         if CCnt>Tbl.ColCount-1 then Continue;
         Tbl.FCells[CCnt,RCnt]:=S1;
         Inc(CCnt);
         if S0[FndInd]=CDMFTblRowEnd then begin
          Inc(RCnt);
          if RCnt>Tbl.RowCount-1 then Continue;
          CCnt:=0;
         end;
        finally
         //переходим на начало след. ячейки
         CurInd:=FndInd+1;
        end;

    end;

   end;

   //разбираем строку параметров объекта
   Inc(CurInd);
   //выполняем цикл пока находим признак завершения параметра
   while FindSubStr(CDMFParEnd,S0,CurInd,Length(S0),EndInd) do
    try
     //получаем индекс параметра в списке
     if not FindSubStr(CDMFParSep,S0,CurInd,EndInd,FndInd) then Continue;
     S1:=Copy(S0,CurInd,FndInd-CurInd);
     //индекс параметра за пределами списка или не числовое значение
     if not CheckNumber(S1,VInt,false) or (VInt<DMFParamHdr.MinService) or
        (VInt>DMFParamHdr.Count) then Continue;
     //создаем новую подпись
     Lbl:=TMapLabel.Create(Obj);
     Lbl.SetParam(FParams[VInt-DMFParamHdr.MinService]);
     CurInd:=FndInd+1;
     //ищем значение подписи
     if FindSubStr([CDMFParInfo,CDMFParEnd],S0,CurInd,EndInd,FndInd)
        then Lbl.FValue:=Copy(S0,CurInd,FndInd-CurInd)
        else Continue;
     CurInd:=FndInd+1;
     InfLen:=0;
     //если подпись отображается -разбираем параметры отображения
     while FindSubStr([CDMFParInfo,CDMFParEnd],S0,CurInd,EndInd,EndInd2) do
      begin
       //признак типа инфо
       I:=UpCase(S0[CurInd]);
       InfInd:=0;
       CurInd:=CurInd+1;
       while FindSubStr([CDMFParMultiSep,CDMFParInfo,
                         CDMFParEnd],S0,CurInd,EndInd2,FndInd) do
        begin
         //само инфо
         S1:=Copy(S0,CurInd,FndInd-CurInd);
         //заменяем разделитель на точку
         S1:=StringReplace(S1,',','.',[]);
         CurInd:=FndInd+1;
         if not CheckNumber(S1,VDbl,false)
          then begin
           Inc(InfInd);
           break;
          end;

         VInt:=SafeTrunc(VDbl);
         N:=Length(Lbl.FInfos);
         if InfInd>=N then SetLength(Lbl.FInfos,N+10);
         case I of
          'X': begin
                Lbl.FInfos[InfInd].ShiftX:=VDbl;
                Inc(InfLen);
               end;
          'Y': Lbl.FInfos[InfInd].ShiftY:=VDbl;
          'Z': Lbl.FInfos[InfInd].ShiftZ:=VDbl;
          'O': Lbl.FInfos[InfInd].Rotate:=VInt;
          'L': //подписи с таким выравниванием не отображаются
               if (CDMFLblNone and VInt)=CDMFLblNone
                then Lbl.FInfos[InfInd].Hidden:=true
                else begin
                 Lbl.FInfos[InfInd].Hidden:=false;
                 Lbl.FInfos[InfInd].HAlign:=haLeft;
                 Lbl.FInfos[InfInd].VAlign:=vaTop;
                 //младший байт -выравн. по гориз.
                 case Lo(VInt) of
                  CDMFLblMiddle:Lbl.FInfos[InfInd].HAlign:=haCenter;
                  CDMFLblRight:Lbl.FInfos[InfInd].HAlign:=haRight;
                 end;
                 //старший байт -выравн. по верт.
                 case Hi(VInt) of
                  CDMFLblCenter:Lbl.FInfos[InfInd].VAlign:=vaCenter;
                  CDMFLblBottom:Lbl.FInfos[InfInd].VAlign:=vaBottom;
                 end;
               end;
          'S': Lbl.FFontSize:=VInt;
          'P': Lbl.FFollowContour:=true;
         end;
         Inc(InfInd);
        end;
       end;
     //длина всех цепочек значений должна быть одинаковая
     //устанавливаем длину по кол-ву значений для параметра X
     SetLength(Lbl.FInfos,Min(Length(Lbl.FInfos),InfLen));
     //записываем нулевые координаты в ограничивающий подпись рект
     Lbl.EmptyBoundRects;
     Lbl.FDisplayed:=Length(Lbl.FInfos)>0;
     //добавляем подпись
     Obj.FLabels.Add(Lbl);
    finally
     CurInd:=EndInd+2;
    end;

   //считываем точки объекта
   if (DMFObj1.Count<0) or
      (DMFObj1.Count>High(Word)) then break;

   SetLength(Obj.FPoints,DMFObj1.Count);
   PntInd:=0;
   while PntInd<DMFObj1.Count do begin

    if S.Read(DMFObjPnt,SizeOf(DMFObjPnt))<>SizeOf(DMFObjPnt)
     then break;

    Obj.FPoints[PntInd].Status:=DMFObjPnt.Status;
    Obj.FPoints[PntInd].X:=DMFObjPnt.X;
    Obj.FPoints[PntInd].Y:=DMFObjPnt.Y;
    Obj.FPoints[PntInd].Z:=DMFObjPnt.Z;
    Inc(PntInd);

   end;

   //не все точки были считаны
   if PntInd<DMFObj1.Count then break;

   //считываем изображение или OLE
   if S.Position<EndPos-8 then begin
    //тип данных, 0 - bitmap, 1 - OLE
    S.Read(DMFImgOLE.Typ,SizeOf(DMFImgOLE.Typ));
    //размер данных
    S.Read(DMFImgOLE.Size,SizeOf(DMFImgOLE.Size));
    if DMFImgOLE.Size>0 then
     case DMFImgOLE.Typ of
      0: begin
          Obj.FImage:=TDIB.Create;
          Obj.FImage.LoadFromStream(S);
         end;
      1: begin
          Obj.FOLE:=TOLEContainer.Create(nil);
          Obj.FOLE.Parent:=Application.MainForm;
          Obj.FOLE.Visible:=false;
          Obj.FOLE.Enabled:=false;
          Obj.FOLE.BorderStyle:=bsNone;
          Obj.FOLE.SizeMode:=smClip;
          Obj.FOLE.LoadFromStream(S);
         end;
     end;
   end;

   //удаленный объект пропускаем
   if DMFObj1.Status and 2 = 0 then begin
    //рассчитываем ограничивающий объект прямоугольник
    Obj.Update;
    //добаляем объект к списку
    AddObject(Obj);
   end;

   Inc(ObjInd);

   if EndPos>S.Position
    //считали меньше данных, чем требуется
    //устанавливаем позицию -начало след. объекта
    then S.Seek(EndPos,soFromBeginning)
    //если считали больше данных, чем требуется -выходим
    else if EndPos<S.Position then break;

  end;

  //если файл поврежден и кол-во считанных объектов
  //не совпадет с заявленным в заголовке карты,
  //сообщим пользователю, что карта будет усечена
  if ObjInd<=DMFMapHdr.Count-1 then Error(SFileTruncated,[DMFMapHdr.Count,ObjInd]);

 finally

  DecimalSeparator:=OldDecSep;

  if S<>F then begin
   //сначала удаляем декомпрессирующий поток,
   //затем закрываем файл
   S.Free; F.Free;
  end else S.Free;

  //записываем вместо индексов ссылки на объекты
  for LrInd:=0 to FLayers.Count-1 do begin
   Layer:=FLayers[LrInd];
   N:=integer(Layer.FSymbol);
   //условный знак слоя
   if (N>0) and (N<=FSymbols.Count)
    then Layer.FSymbol:=FSymbols[N-1]
    else Layer.FSymbol:=nil;
   for Ind:=Layer.FAvailableParams.Count-1 downto 0 do begin
    N:=integer(Layer.FAvailableParams[Ind]);
    //параметры доступные слою
    if (N>=0) and (N<FParams.Count)
     then begin
      Param:=FParams[N];
      Layer.FAvailableParams[Ind]:=Param;
      //слои в списке AvailableParams которых находится параметр
      Param.FLayers.Add(Layer);
     end else Layer.FAvailableParams.Delete(Ind);
   end;
  end;
  //тоже для параметров
  for ParInd:=0 to FParams.Count-1 do begin
   Param:=FParams[ParInd];
   N:=integer(Param.FSymbol);
   //условный знак слоя
   if (N>0) and (N<=FSymbols.Count)
    then Param.FSymbol:=FSymbols[N-1]
    else Param.FSymbol:=nil;
  end;

 //устанавливаем рамку карты
 R.Bottom:=DMFMapHdr.Frame[0].X;
 R.Left:=DMFMapHdr.Frame[0].Y;
 R.Top:=DMFMapHdr.Frame[2].X;
 R.Right:=DMFMapHdr.Frame[2].Y;
 SetFrame(R);

 //пересчет значений подписей
 Recalc;

 end;

end;


end.
