среда, 18 сентября 2013 г.

Мне вот кстати интересно

Интересен ли аналог TRichEdit под FireMonkey? Я - "знаю" как его "быстро" сделать.

P.S. http://18delphi.blogspot.ru/search/label/%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D0%B5%D0%BB%D1%8F%D0%BC%20%D1%80%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%BE%D1%80%D0%BE%D0%B2

"Я бы лично посоветовал бы поглядеть в сторону CoreText и AttributedString от xCode.

Ну и в сторону - GoF."

"Не смотрите в сторону RTF как "модели документа в памяти". Смотрите на него не более чем на ВНЕШНИЙ формат. Внутри раскладывайте в СВОИ структуры. Лучше в древесные. И описываемые DTD. Смотрите в сторону XML. Это не значит, что "надо сделать кальку с XML". Конечно же - НЕТ. СМОТРИТЕ на него. Анализируйте и рождайте собственные БИНАРНЫЕ структуры "по образу и подобию"."

В общем - что надо сделать? Написать рендеринг ОТДЕЛЬНО взятого текстового параграфа. С текстом и шрифтовым оформлением. Всё остальное - из него выводится. А писать там в общем - несложно. Прямо как "кальку" с CoreText. Параграф с оформлением -> Frame -> DrawFrame.

Как писать? Взять GoF и "концепцию Glyph'ов".

Мой привет. Далеко не идеальный:

type
 TnevTextParaRenderInfo = class(TnevLeafRenderInfo)
 private
 // private fields
   f_L : InevLines;
   f_VS : InevTag;
   f_Children : TnevFormatInfoList;
 protected
 // overridden property methods
   function pm_GetViewSegmentsPlace: InevTag; override;
   function pm_GetLinesPlace: InevLines; override;
   procedure pm_SetLinesPlace(const aValue: InevLines); override;
   function pm_GetLocSpacing: TnevRect; override;
 protected
 // overridden protected methods
   procedure Cleanup; override;
     {* Функция очистки полей объекта. }
   function GetMaxLinesCount: Integer; override;
   procedure DoRecalc(const aView: InevViewMetrics); override;
 public
 // overridden public methods
   function GetInfoForChild(const aChild: InevObjectPrim): TnevFormatInfo; override;
   procedure DoInvalidateShape(const aShape: InevObject;
    aParts: TnevShapeParts); override;
   procedure WForce(aParts: TnevRenderingInfoParts); override;
 end;//TnevTextParaRenderInfo

...

procedure TnevTextParaRenderInfo.Cleanup;
//#UC START# *479731C50290_481F174D01BF_var*
//#UC END# *479731C50290_481F174D01BF_var*
begin
//#UC START# *479731C50290_481F174D01BF_impl*
 if (f_Children <> nil) then
  f_Children.ClearReferencesToParentFormatInfo;
 FreeAndNil(f_Children);
 f_L := nil;
 f_VS := nil;
 inherited;
//#UC END# *479731C50290_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.Cleanup

function TnevTextParaRenderInfo.GetInfoForChild(const aChild: InevObjectPrim): TnevFormatInfo;
//#UC START# *4815C94A027A_481F174D01BF_var*
var
 l_Index : Integer;
 l_Item  : TnevFormatInfo;  
//#UC END# *4815C94A027A_481F174D01BF_var*
begin
//#UC START# *4815C94A027A_481F174D01BF_impl*
 Assert(aChild.InheritsFrom(k2_idLeafPara) OR
        aChild.InheritsFrom(k2_idTextSegment));
 if (f_Children = nil) then
  f_Children := TnevFormatInfoList.Create;
 with f_Children do
 begin
  for l_Index := 0 to Pred(Count) do
  begin
   l_Item := Items[l_Index];
   if l_Item.Obj.IsSame(aChild) then
   begin
    Result := l_Item;
    Exit;
   end;//l_Item.f_Obj.IsSame(aShape)
  end;//for l_Index
  l_Item := TnevFormatInfoFactory.CreateFormatInfo(aChild, Self, Metrics);
  try
   l_Item.LimitWidth := LimitWidth;
          // ^ - может быть здесь надо отступы учесть
   Add(l_Item);
   Result := l_Item;
  finally
   FreeAndNil(l_Item);
  end;//try..finally
 end;//with f_Children
//#UC END# *4815C94A027A_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.GetInfoForChild

procedure TnevTextParaRenderInfo.DoInvalidateShape(const aShape: InevObject;
  aParts: TnevShapeParts);
//#UC START# *48172A690313_481F174D01BF_var*
//#UC END# *48172A690313_481F174D01BF_var*
begin
//#UC START# *48172A690313_481F174D01BF_impl*
 if (nev_spSegments in aParts) then
 begin
  if (f_VS <> nil) then
  begin
   f_VS := nil;
   Include(aParts, nev_spExtent);
  end;//f_VS <> nil
 end;//nev_spSegments in aParts
 inherited;
//#UC END# *48172A690313_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.DoInvalidateShape

procedure TnevTextParaRenderInfo.WForce(aParts: TnevRenderingInfoParts);
//#UC START# *48175C1302A3_481F174D01BF_var*
//#UC END# *48175C1302A3_481F174D01BF_var*
begin
//#UC START# *48175C1302A3_481F174D01BF_impl*
 if (([nev_ripWidth, nev_ripLines, nev_ripHeight] * aParts) <> []) then
  FreeAndNil(f_Children);
 inherited;
//#UC END# *48175C1302A3_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.WForce

function TnevTextParaRenderInfo.pm_GetViewSegmentsPlace: InevTag;
//#UC START# *4821DB2500CB_481F174D01BFget_var*
var
 l_Super       : InevTag absolute Result;
 l_SuperHandle : Integer;
 l_Exclude     : TevNormalSegLayerHandleSet;
 l_TextPara    : InevTextPara;

 function _AddLayer(const SegLst: InevTag; Index: Integer): Boolean; 

  function _AddSegment(const Seg: InevTag; Index: Integer): Boolean; 
  var
   S1 : InevTag;
  begin
   S1 := Seg.CloneTag;
   try
    evSegmentsLayer_AddSegment(l_TextPara, l_Super, S1);
   finally
    S1 := nil;
   end;{try..finally}
   Result := true;
  end;{_AddSegment}

 var
  l_Handle : Integer; 
 begin
  l_Handle := SegLst.IntA[k2_tiHandle];
  if (l_Handle > ev_slSuperposition) AND not (l_Handle in l_Exclude) then
   SegLst.IterateChildrenF(k2L2TIA(@_AddSegment));
  Result := true;
 end;{_AddLayer}

var
 l_Segments : InevTag;
 l_CC       : Integer;
//#UC END# *4821DB2500CB_481F174D01BFget_var*
begin
//#UC START# *4821DB2500CB_481F174D01BFget_impl*
 if (f_VS = nil) then
 begin
  if not Obj.QT(InevTextPara, l_TextPara) then
   Assert(false);
  l_Segments := l_TextPara.Attr[k2_tiSegments];
  if l_Segments.IsValid then
  begin
   if not l3IsNil(l_TextPara.Text) then
   begin
    l_CC := l_Segments.ChildrenCount;
    if (l_CC <= 0) then
    begin
     // - нет никакого оформления, значит и суперпозиция не нужна
     Result := k2NullTag;
     l_TextPara.Attr[k2_tiSegments] := nil;
     // - зачищаем странные сегменты
    end//l_CC <= 0
    else
    begin
     l_SuperHandle := 0;
     if Self.Parent <> nil then
      l_Exclude := Metrics.ExcludeSuper
     else
      l_Exclude := [];
     if (l_Exclude = []) AND (l_CC = 1) then
     begin
      // - не нужно копировать сегменты в суперпозицию
      Result := l_Segments.Child[0];
     end
     else
     begin
      Result := l_Segments.rAtomEx([k2_tiChildren, k2_tiHandle, l_SuperHandle]);
      if (Result = nil) OR not Result.IsValid then
      begin
       Result := l_Segments.cAtomEx([k2_tiChildren, k2_tiHandle, l_SuperHandle], nil);
       l_Segments.IterateChildrenF(k2L2TIA(@_AddLayer));
       l_Segments.DeleteChild(Result);
      end//not Result.IsValid
      else
       Assert(false);
     end;//l_Exclude = []..
    end;//l_CC <= 0
   end//not l3IsNil(Text)
   else
    Result := k2NullTag;
  end//l_Segments.IsValid
  else
   Result := k2NullTag;
  f_VS := Result;
 end//f_VS = nil
 else
  Result := f_VS;
//#UC END# *4821DB2500CB_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetViewSegmentsPlace

function TnevTextParaRenderInfo.pm_GetLinesPlace: InevLines;
//#UC START# *4821DE24003D_481F174D01BFget_var*
//#UC END# *4821DE24003D_481F174D01BFget_var*
begin
//#UC START# *4821DE24003D_481F174D01BFget_impl*
 Result := f_L;
//#UC END# *4821DE24003D_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetLinesPlace

procedure TnevTextParaRenderInfo.pm_SetLinesPlace(const aValue: InevLines);
//#UC START# *4821DE24003D_481F174D01BFset_var*
//#UC END# *4821DE24003D_481F174D01BFset_var*
begin
//#UC START# *4821DE24003D_481F174D01BFset_impl*
 f_L := aValue;
//#UC END# *4821DE24003D_481F174D01BFset_impl*
end;//TnevTextParaRenderInfo.pm_SetLinesPlace

function TnevTextParaRenderInfo.GetMaxLinesCount: Integer;
//#UC START# *4BC45843011E_481F174D01BF_var*
var
 l_Lines: InevLines;
//#UC END# *4BC45843011E_481F174D01BF_var*
begin
//#UC START# *4BC45843011E_481F174D01BF_impl*
 l_Lines := Lines;
 if l_Lines = nil then         
  Result := 1
 else
 begin
  Result := l_Lines.Count;
  Assert(Result > 0);
 end;//l_Lines = nil
//#UC END# *4BC45843011E_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.GetMaxLinesCount

function TnevTextParaRenderInfo.pm_GetLocSpacing: TnevRect;
//#UC START# *4E5F3D1102B8_481F174D01BFget_var*
const
 cnTopSpace = nevInch div 8;
//#UC END# *4E5F3D1102B8_481F174D01BFget_var*
begin
//#UC START# *4E5F3D1102B8_481F174D01BFget_impl*
 Result := inherited pm_GetLocSpacing;
 if (Metrics.AACLike = nev_aacLeft) and EvIsParaHyperlink(Obj) then
 begin
  Result.Top := Result.Top + cnTopSpace;
  Result.Bottom := Result.Bottom + cnTopSpace;
 end;// if (Metrics.AACLike = nev_aacLeft) and EvIsParaHyperlink(Obj) then
//#UC END# *4E5F3D1102B8_481F174D01BFget_impl*
end;//TnevTextParaRenderInfo.pm_GetLocSpacing

procedure TnevTextParaRenderInfo.DoRecalc(const aView: InevViewMetrics);
//#UC START# *4E7094780214_481F174D01BF_var*
var
 l_Height       : Integer;
 l_InfoCanvas   : InevInfoCanvas;
 l_Map          : TevLineArray;
 l_InLimitWidth : Integer;

 procedure DoFormat{(const ETAOIN_SHRDLU_aSegmentAndFontTool: InevFontTool)};   
 var
  l_SoftEnterCount : Integer;
  l_Str            : Tl3PCharLen;    // - Строка параграфа.
  l_PrevWrap       : PAnsiChar;
  l_FontIndexes    : Tl3FontIndexes;
  l_AllowHyphen    : Boolean;
  l_ITabInfo       : Il3TabInfo;
  l_MTabInfo       : Il3TabInfo;
  l_MeasureCanvas  : InevInfoCanvas;
  l_CharSize       : Integer;
  l_Spaces         : TnevRect;

  procedure FormatStr(var theLimitWidth : Integer;
                      const aCurFont    : InevFont;
                      IsSeg             : Boolean;
                      var aStr          : Tl3PCharLen;
                      var theStrExtent  : TnevPoint);   
  var
   l_AddHyphen : Boolean;
   l_StrExtent : TnevPoint;

   procedure AddLine;
   begin{AddLine}
    l_PrevWrap := aStr.S;
    if (theStrExtent.Y = 0) then
     theStrExtent.Y := l_MeasureCanvas.AverageCharHeight;
    l_Map.Add((aStr.S - l_Str.S) div l_CharSize,
              theStrExtent, l_FontIndexes, l_AddHyphen);

    l_InfoCanvas.FinishTabs(l_ITabInfo);
    l_InfoCanvas.StartTabs(l_ITabInfo, Obj.TabStops);
    // - Переоткрываем новую строку

    if (l_InfoCanvas <> l_MeasureCanvas) then 
    begin
     l_MeasureCanvas.FinishTabs(l_MTabInfo);
     l_MeasureCanvas.StartTabs(l_MTabInfo, Obj.TabStops);
     // - Переоткрываем новую строку
    end;//l_InfoCanvas <> l_MeasureCanvas

    Inc(l_Height, theStrExtent.Y);
    theStrExtent := nevPt0;
    l_StrExtent := nevPt0;
    l_FontIndexes := [l_InfoCanvas.Font.Index];
    theLimitWidth := l_InLimitWidth - l_Spaces.Left;
   end;//AddLine

  var
   l_WrappedStr : Tl3PCharLen;
   l_OTabInfo   : Il3TabInfo;

   procedure MeasureStr(aNeedTrim: Boolean);
   var
    l_Str     : Tl3WString;
    l_TabInfo : Il3TabInfo;
    l_Len     : Integer;
   begin//MeasureStr
    if aNeedTrim then
     l_Str := l3RTrim(l_WrappedStr)
    else
     l_Str := l_WrappedStr;
    if (l_InfoCanvas = l_MeasureCanvas) then
    begin
     l_MeasureCanvas.StartTabs(l_TabInfo, l_OTabInfo);
     with l_MeasureCanvas.TextExtent(l_Str).P do
     begin
      Inc(theStrExtent.P.X, X);
      Inc(l_StrExtent.P.X, X);
      if l_AddHyphen then
      begin
       Inc(theStrExtent.P.X, evHyphenWidth);
       Inc(l_StrExtent.P.X, evHyphenWidth);
      end;//l_AddHyphen
      if (Y > theStrExtent.Y) then
      begin
       theStrExtent.Y := Y;
       l_StrExtent.Y := Y;
      end;//Y > theStrExtent.Y
     end;//with l_MeasureCanvas.TextExtent(l_Str).P
     l_MeasureCanvas.FinishTabs(l_TabInfo);
    end//l_InfoCanvas = l_MeasureCanvas
    else
    begin
     l_MeasureCanvas.Font := l_InfoCanvas.Font;
     l_MeasureCanvas.StartTabs(l_TabInfo, l_OTabInfo);
     with l_MeasureCanvas.TextExtent(l_Str).P do
     begin
      Inc(theStrExtent.P.X, X);
      if l_AddHyphen then 
       Inc(theStrExtent.P.X, evHyphenWidth);
      if (Y > theStrExtent.Y) then
       theStrExtent.Y := Y;
     end;//with l_MeasureCanvas.TextExtent(l_Str).P
     l_MeasureCanvas.FinishTabs(l_TabInfo);
     l_InfoCanvas.StartTabs(l_TabInfo, l_OTabInfo);
     with l_InfoCanvas.TextExtent(l_Str).P do
     begin
      Inc(l_StrExtent.P.X, X);
      if l_AddHyphen then
       Inc(l_StrExtent.P.X, evHyphenWidth);
      if (Y > l_StrExtent.Y) then
       l_StrExtent.Y := Y;
     end;//with l_MeasureCanvas.TextExtent(l_Str).P
     l_InfoCanvas.FinishTabs(l_TabInfo);
    end;//l_InfoCanvas = l_MeasureCanvas
   end;//MeasureStr

  var
   l_WrapPos      : Integer;  { - Позиция соответствующая максимальной ширине. }
   l_SoftEnterStr : PAnsiChar; { - Позиция SoftEnter'а. }
   l_NoTabs       : Boolean;
   l_InTable      : Boolean;
  begin//FormatStr
   l_StrExtent := theStrExtent;
   with l_InfoCanvas do
   begin
    Font := aCurFont;
    Include(l_FontIndexes, Font.Index);
   end;//with l_InfoCanvas
   l_NoTabs := False;
   while True do
   begin
    l_AddHyphen := False;
    l_OTabInfo := l_InfoCanvas.TabInfo;
    l_WrapPos := l_InfoCanvas.Pos2IndexQ(theLimitWidth - l_StrExtent.X, aStr, l_NoTabs);
    Assert(l_WrapPos >= 0);
    //l_WrapPos := l_InfoCanvas.Pos2Index(theLimitWidth - l_StrExtent.X, aStr);
    if (l_WrapPos <= 0) then
    begin
     if (l_StrExtent.X <= 0) then
      l_WrapPos := 1
     else
     if l3Same(aStr, l_Str) then
     //if (aStr.S = l_Str.S) then
     // - если често - я не до конца понял, что это за проверка и какая из двух
     //   проверок правильнее.
      Break
{     else
     if (aStr.SLen > 0) then
      Assert(false)};
    end;//l_WrapPos <= 0
    l_WrappedStr := aStr;
    if (l_WrapPos >= aStr.SLen) then
     l_WrappedStr.SLen := l_WrapPos
    else
     l_WrappedStr.SLen := l3Utils.l3FindNextLine(aStr, l_WrapPos);
    // - Нашли где должна кончаться строка без учета SoftEnter.
    if (l_SoftEnterCount > 0) then
    begin
     // - Еще есть SoftEnter'ы - надо их обработать.
     l_SoftEnterStr := ev_lpScan(cc_SoftEnter, l_WrappedStr.S, l_WrappedStr.SLen);
     if (l_SoftEnterStr <> nil) then
     begin
      Dec(l_SoftEnterCount);
      l_WrappedStr.SLen := l_SoftEnterStr - l_WrappedStr.S;
      aStr.Init(l_SoftEnterStr + 1, aStr.SLen - (l_WrappedStr.SLen + 1), aStr.SCodePage);
      MeasureStr(False); // - Измеряем размеры полученной строки.
      AddLine;  // - Добавляем строку с SoftEnter'ом.
      if (aStr.SLen = 0) then
       Break // - Строка закончилась
      else
       Continue; // - Продолжаем форматировать остаток строки.
     end;//l_SoftEnterStr <> nil
    end;//l_SoftEnterCount > 0
    l_InTable := Obj.Owner.IsValid and Obj.Owner.InheritsFrom(k2_idTableCell); 
    if (l_WrapPos < aStr.SLen) then
    begin
     // - Обрабатываем переносы по слогам
     if l_AllowHyphen then
     begin
      case l3SplitBySlog(aStr, l_WrapPos, l_InTable) of
       l3_ssHyphen : l_WrappedStr.SLen := l_WrapPos;
       l3_ssYes    :
       begin
        l_WrappedStr.SLen := l_WrapPos;
        l_AddHyphen := True;
       end;//l3_ssYes
      end;//case l3SplitBySlog(aStr, l_WrapPos)
     end//l_AllowHyphen
     else
     (*with l_WrappedStr do
      if not evWhiteSpace(S[Pred(SLen)]) then*)
      if (l_StrExtent.X <= 0) then
        l_AddHyphen := l3SplitBySlog(aStr, l_WrappedStr.SLen, l_InTable) = l3_ssYes;
    end;//l_AllowHyphen
    aStr.Shift(l_WrappedStr.SLen);
    if (aStr.SLen = 0) then
    begin
     // - Строка вся кончилась. 
     MeasureStr(False); // - Измеряем размеры полученной строки. 
     Break; // - Выходим, т.к. добавлять нецелую строку не надо. 
    end;//aStr.SLen
    if not l_AddHyphen AND
       // не было переноса
       (l_WrappedStr.S > l_PrevWrap) AND
       (l_WrappedStr.SLen > 0) then
       // - предыдущие две проверки защищают от зацикливания и выхода за границы
    begin
     if not l3IsWhiteSpace((aStr.S-1)^) then
      // - разрезали посередине слова
      if l3IsWhiteSpace((l_WrappedStr.S-1)^)
          // - пробел был где-то раньше
          OR IsSeg
          // - это сегмент, который не надо резать пополам
         then
     begin
      // - Учитываем ситуацию когда сегмент режется посередине слова
      aStr.Init(l_WrappedStr.S, aStr.SLen + l_WrappedStr.SLen, aStr.SCodePage);
      l_WrappedStr.SLen := 0;
     end;//evWhiteSpace(l_WrappedStr.S-1)^)
    end;//not l_AddHyphen..
    MeasureStr(True); // - Измеряем размеры полученной строки. 
    AddLine; // - Добавляем полученную строку. 
   end;//while True
  end;//FormatStr

 var
  l_CurStr     : Tl3PCharLen;
  l_StrExtent  : TnevPoint;
  l_LimitWidth : Integer;
  l_ParaFont   : InevFont;

  function FormatSegment(const aSegment: InevTag; Index: Integer): Boolean;
  var
   l_SegFont   : InevFont;
   l_SegStart  : Integer;
   l_SegFinish : Integer;
  begin//FormatSegment
   Result := True;
   with aSegment do
   begin
    l_SegStart := Pred(IntA[k2_tiStart]);
    l_SegFinish := IntA[k2_tiFinish];
   end;//with aSegment
   { Обрабатываем часть строки до выделения: }
   l_CurStr.SLen := Min(l_SegStart, l_Str.SLen) - (l_CurStr.S - l_Str.S);
   if (l_CurStr.SLen > 0) then
    FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
   { Обрабатываем отрезок выделения. }
   l_CurStr.SLen := Min(l_SegFinish - l_SegStart,
                        l_Str.SLen - (l_CurStr.S - l_Str.S));
   if (l_CurStr.SLen > 0) then
   begin
    //evCheckRenderedObject(aView, aSegment, Obj, aSegmentAndFontTool, Self);
    l_SegFont := Self.InfoForChild(TnevSegmentObject.Make(aSegment)).Font[true];
    //l_SegFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, aSegment, true, Self);
    try
     FormatStr(l_LimitWidth, l_SegFont, true, l_CurStr, l_StrExtent);
    finally
     l_SegFont := nil;
    end;//try..finally
   end;//l_CurStr.SLen > 0
  end;//FormatSegment

  function CheckSegmentHeight(const aSegment: InevTag; Index: Integer): Boolean;
  var
   l_SegFont   : InevFont;
   l_SegStart  : Integer;
   l_SegFinish : Integer;
  begin//
   Result := True;
   with aSegment do
   begin
    l_SegStart := Pred(IntA[k2_tiStart]);
    l_SegFinish := IntA[k2_tiFinish];
   end;//with aSegment
   l_CurStr.S := l_Str.S + l_SegStart;
   l_CurStr.SLen := l_SegFinish - l_SegStart;
   if (l_CurStr.SLen > 0) then
   begin
    //evCheckRenderedObject(aView, aSegment, Obj, aSegmentAndFontTool, Self);
    l_SegFont := Self.InfoForChild(TnevSegmentObject.Make(aSegment)).Font[true];
    //l_SegFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, aSegment, true, Self);
    try
     l_MeasureCanvas.Font := l_SegFont;
     l_Height := Max(l_Height, l_MeasureCanvas.TextExtent(l_Str).Y);
    finally
     l_SegFont := nil;
    end;//try..finally
   end;//l_CurStr.SLen > 0
  end;//CheckSegmentHeight

 var
  l_FirstIndent : Integer;
  l_SLI         : Tl3SingleLineInfo;
 begin//DoFormat
  (*if (RecalcThread <> nil) AND not RecalcThread.ChangeCanvas then
   l_MeasureCanvas := l_InfoCanvas
  else *)begin
   l_MeasureCanvas := aView.InfoCanvas;
   //l_MeasureCanvas := l_InfoCanvas;
   // http://mdp.garant.ru/pages/viewpage.action?pageId=88639598
   // - может статься, что что-то не доделал
   // Точно не доделал:
   // http://mdp.garant.ru/pages/viewpage.action?pageId=195758154
   //l_MeasureCanvas := ETAOIN_SHRDLU_l3CrtIC;
   if l_InfoCanvas.EQ(l_MeasureCanvas) then
    l_MeasureCanvas := l_InfoCanvas;
  end;//RecalcThread <> nil
  l_Spaces := Self.Get_Spacing;
  l_Map := TevLineArray.Create;
  try
   Self.wMap(l_Map);
  finally
   l_Map := l_Map.Free;
  end;//try..finally
  try
   if not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj) then
   begin
    l_AllowHyphen := Obj.BoolA[k2_tiAllowHyphen];
    l_Str := Obj.Text;
    if (l_Str.SCodePage = CP_Unicode) then
     l_CharSize := SizeOf(WideChar)
    else
     l_CharSize := SizeOf(ANSIChar);
    l_ParaFont := Self.Get_Font(true);
    //l_ParaFont := aSegmentAndFontTool.ETAOIN_SHRDLU_GetObjIFont(aView, Obj, False, Self);
    try
     l_InfoCanvas.Font := l_ParaFont;
     if (l_InfoCanvas <> l_MeasureCanvas) then
      l_MeasureCanvas.Font := l_ParaFont;
     l_InfoCanvas.StartTabs(l_ITabInfo, Obj.TabStops);
     if (l_InfoCanvas <> l_MeasureCanvas) then
      l_MeasureCanvas.StartTabs(l_MTabInfo, Obj.TabStops);
     try
      Dec(l_InLimitWidth, l_Spaces.Right);
      if l3IsNil(l_Str) then
      begin
       l_Height := l_MeasureCanvas.AverageCharHeight;
       l_Map.Add(0, Tl3_Point_C(0, l_Height));
       if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) then
        Self.wWidth(0)
       else
        Self.wWidth(l_InLimitWidth);
      end//l3IsNil(l_Str)
      else
      if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) and Obj.IsValid then
      begin
       l_StrExtent := l_MeasureCanvas.TextExtent(l_Str);
       l_Height := l_StrExtent.Y;
       with Self.ViewSegments do
        if IsValid then
        begin
         IterateChildrenF(k2L2TIA(@CheckSegmentHeight));
         l_StrExtent.Y := l_Height;
        end;//IsValid
       // Не уверен, что такая проверка нужна... 
       l_Map.Add(l_Str.SLen, l_StrExtent);
       Self.wWidth(l_StrExtent.X + l_Spaces.Left);
      end//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
      else
      begin
       Self.wWidth(l_InLimitWidth);
       l_FirstIndent := Self.Get_FirstIndent;
       if (l_Spaces.Left >= l_InLimitWidth - 100) OR
          (l_FirstIndent >= l_InLimitWidth - 100) then
       begin
        l_Height := l_MeasureCanvas.AverageCharHeight;
        l_Map.Add(0, Tl3_Point_C(0, l_Height));
       end//l_Spaces.Left >= l_InLimitWidth - 100
       else
       begin
        l_Height := 0;
        l_StrExtent := nevPt0;
        l_LimitWidth := l_InLimitWidth - l_FirstIndent;
        l_FontIndexes := [];
        l_SoftEnterCount := l3CountOfChar(cc_SoftEnter, l_Str);
        l_CurStr := l_Str;
        l_PrevWrap := l_Str.S;
        with Self.ViewSegments do
         if IsValid then
         begin
          IterateChildrenF(k2L2TIA(@FormatSegment));
          l_CurStr.SLen := l_Str.SLen - (l_CurStr.S - l_Str.S);
          FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
          // - доформатируем хвост после последнего сегмента
         end//IsValid
         else
          FormatStr(l_LimitWidth, l_ParaFont, false, l_CurStr, l_StrExtent);
        if (l_StrExtent.Y <= 0) then
        begin
         l_MeasureCanvas.Font := l_ParaFont;
         l_StrExtent.Y := l_MeasureCanvas.AverageCharHeight;
        end;//l_StrExtent.Y <= 0
        l_Map.Add(l_Str.SLen, l_StrExtent, l_FontIndexes, false);
        Inc(l_Height, l_StrExtent.Y);
       end;//l_Spaces.Left >=..
      end;//..ev_itPreformatted..
     finally
      if (l_InfoCanvas <> l_MeasureCanvas) then
       l_MeasureCanvas.FinishTabs(l_MTabInfo);
      l_InfoCanvas.FinishTabs(l_ITabInfo);
     end;//try..finally
    finally
     l_ParaFont := nil;
    end;//try..finally
 (*   with Obj.rAtomEx([k2_tiFrame, k2_tiFrameUp]) do
     if IsValid then
      Inc(l_Height, IntA[k2_tiWidth]);*)
   end//not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj)
   else
   begin
    Self.wWidth(0);
    l_Height := 0;
   end//not Obj.TreatCollapsedAsHidden OR not aView.IsTagCollapsed(Obj)
   ;
   Inc(l_Height, l_Spaces.Top);
   Inc(l_Height, l_Spaces.Bottom);
  finally
   if (l_Map <> nil) then
   begin
    if (l_Map.Count < 1) then
    begin
     // - удаляем ненужное в данном случае форматирование
     l_Map := nil;
     Self.wMap(nil);
    end//l_Map.Count < 1
    else
    if (l_Map.Count = 1) then
    begin
     if (Obj.IntA[k2_tiJustification] = Ord(ev_itPreformatted)) then
     begin
      // - удаляем ненужное в данном случае форматирование
      l_Map := nil;
      Self.wMap(nil);
     end//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
     else
     begin
      with PevLineInfo(l_Map.Items[0])^ do
       l_SLI := Tl3SingleLineInfo.Make(LE.X, LE.Y, FI);
      try
       l_Map := nil;
       Self.wMap(l_SLI);
      finally
       FreeAndNil(l_SLI);
      end;//try..finally
     end;//IntA[k2_tiJustification] = Ord(ev_itPreformatted)
    end;//l_Map.Count <= 1
   end;//l_Map <> nil
  end;//try..finally
 end;//DoFormat

{var
 l_SegmentAndFontTool : InevFontTool;}
//#UC END# *4E7094780214_481F174D01BF_var*
begin
//#UC START# *4E7094780214_481F174D01BF_impl*
 l_InLimitWidth := Self.rLimitWidth;
 l_Height := 0;
 Obj.DoLoad;
 l_InfoCanvas := aView.FormatCanvas;
// l_InfoCanvas := aView.InfoCanvas;
 try
  if Self.IsHidden(false,
                  (l_InfoCanvas <> nil) AND
                  l_InfoCanvas.Printing) then
  begin
   Self.wWidth(0);
   Self.wMap(nil);
  end//Obj.IsHiddenPrim(Self, aView.HiddenStyles)
  else
  begin
(*   l_SegmentAndFontTool := aView.FontTool{ As InevFontTool};
   try*)
(*    l_SegmentAndFontTool.ForPrinting := (l_InfoCanvas.Printing
                                         {$IFNDef Nemesis}OR not aView.IsWebStyle{$EndIf});*)
    l_InfoCanvas.Lock;
    try
     l_InfoCanvas.PushBC;
     l_InfoCanvas.PushFC;
     // - Запоминаем - чтобы не испортить фон для отрисовки
     try
      DoFormat{(l_SegmentAndFontTool)};
     finally
      l_InfoCanvas.PopFC;
      l_InfoCanvas.PopBC;
     end;//try..finally
    finally
     l_InfoCanvas.Unlock;
    end;//try..finally
(*   finally
    l_SegmentAndFontTool := nil;
   end;//try..finally*)
  end;//not Obj.IsHiddenPrim(Self, aView.HiddenStyles)
 finally
  l_InfoCanvas := nil;
 end;//try..finally
 Self.wHeight(l_Height);
//#UC END# *4E7094780214_481F174D01BF_impl*
end;//TnevTextParaRenderInfo.DoRecalc

P.P.S. Говоря о "портянках кода" - наверное теперь понятно, что я имею в виду. Но я (пока) не знаю - как это улучшить.

P.P.P.S. Этот код в таком (или примерно таком) виде "тянется" аж с 1996 (!) года. Я бы хотел его улучшить. Но пока - не понял как. При всей моей любви к "паттернам". Интересно было бы заглянуть ВНУТРЬ CoreText. И его Frame и DrawFrame.

P.P.P.P.S. Многое менялось за эти годы. COM|CORBA пришли и "почти ушли". Интерфейсы. Примеси. Шаблоны. UML. Но этот код как был "странным". Так и остался. Зато - "вполне эффективным". Это ещё вы не видели кода "рисования фрейма". Там - "ещё веселее".

Кстати. Вот и он:

procedure TevTextParaPainterEx.CheckDrawFocused;
//#UC START# *502268560274_49DA30D901B6_var*
//#UC END# *502268560274_49DA30D901B6_var*
begin
//#UC START# *502268560274_49DA30D901B6_impl*
 if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) then
  Area.rCanvas.NotFocused := False;
//#UC END# *502268560274_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckDrawFocused

procedure TevTextParaPainterEx.CheckInvertInLeftAAC;
//#UC START# *5052EF710307_49DA30D901B6_var*
//#UC END# *5052EF710307_49DA30D901B6_var*
begin
//#UC START# *5052EF710307_49DA30D901B6_impl*
 if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
  Area.rCanvas.Font.ForeColor := clBlack
//#UC END# *5052EF710307_49DA30D901B6_impl*
end;//TevTextParaPainterEx.CheckInvertInLeftAAC

procedure TevTextParaPainterEx.DrawArrow(aRectHeight: Integer);
//#UC START# *50A4D6E90261_49DA30D901B6_var*
var
 l_Delta     : Integer;
 l_ParaH     : Integer;
 l_Width     : Integer;
 l_Height    : Integer;
 l_ImageInfo : PnevControlImageInfo;
//#UC END# *50A4D6E90261_49DA30D901B6_var*
begin
//#UC START# *50A4D6E90261_49DA30D901B6_impl*
 if (Area.rView.As_InevView.Metrics.AACLike = nev_aacLeft) and Area.rCanvas.Invert and ParentPainter.IsSelectedOneWholePara(ParaX) then
  with Area.rCanvas do
  begin
   l_ImageInfo := FormatInfo.ImageInfo;
   if l_ImageInfo <> nil then
   begin
    l_Height := DP2LP(PointX(l_ImageInfo.rImageList.Height)).X;
    l_ParaH := (aRectHeight + Spaces.Top + Spaces.Bottom);
    l_Delta := (l_ParaH - l_Height) div 2;
    l_Delta := l_Delta + l_Height - l_ParaH + Spaces.Bottom;
    l_Width := DP2LP(PointX(l_ImageInfo.rImageList.Width)).X;
    MoveWindowOrg(l3Point(l_Width + (l3Inch div 16), l_Delta));
    FillRect(l3SRect(0, 0, l_ImageInfo.rImageList.Width, l_ImageInfo.rImageList.Height));
    l_ImageInfo.rImageList.Draw(As_Il3Canvas, 0, 0, l_ImageInfo.rFirstIndex);
   end; // if l_ImageInfo <> nil then
  end; // if Area.rView.As_InevView.Metrics.AACLike then
//#UC END# *50A4D6E90261_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawArrow

procedure TevTextParaPainterEx.ValidateSegFont(const aSeg: InevTag);
//#UC START# *4D664903006E_49DA30D901B6_var*
//#UC END# *4D664903006E_49DA30D901B6_var*
begin
//#UC START# *4D664903006E_49DA30D901B6_impl*
 if (Area.rView.As_InevView.Metrics.AACLike > nev_aacNone) and Area.rCanvas.Invert then
  if aSeg.InheritsFrom(k2_idHyperlink) then
    Area.rCanvas.Font.ForeColor := clBlack;
//#UC END# *4D664903006E_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateSegFont

procedure TevTextParaPainterEx.ValidateParaFont;
//#UC START# *4D725A550218_49DA30D901B6_var*
//#UC END# *4D725A550218_49DA30D901B6_var*
begin
//#UC START# *4D725A550218_49DA30D901B6_impl*
 // ничего не делаем
//#UC END# *4D725A550218_49DA30D901B6_impl*
end;//TevTextParaPainterEx.ValidateParaFont

procedure TevTextParaPainterEx.Release;
//#UC START# *479F2AFB0397_49DA30D901B6_var*
//#UC END# *479F2AFB0397_49DA30D901B6_var*
begin
//#UC START# *479F2AFB0397_49DA30D901B6_impl*
 FreeAndNil(f_LinePainter);
 inherited;
//#UC END# *479F2AFB0397_49DA30D901B6_impl*
end;//TevTextParaPainterEx.Release

procedure TevTextParaPainterEx.GetFramePartPrim(const aFrame: InevTag;
  anIndex: Tl3FramePartIndex;
  var thePart: TnevFramePart);
//#UC START# *4804B9BB0383_49DA30D901B6_var*
var
 l_Style : InevTag;
 l_N     : InevPara;
//#UC END# *4804B9BB0383_49DA30D901B6_var*
begin
//#UC START# *4804B9BB0383_49DA30D901B6_impl*
 inherited;
 if (anIndex = l3_fpiUp) then
  if (Spaces.Top > 0) AND not ParaX.Attr[k2_tiStyle].BoolA[k2_tiHeaderHasOwnSpace] then
  begin
   thePart.rDrawType := lpdDraw;
   thePart.rColor := clBlack;
   thePart.rWidth := 0;
   thePart.rSpaceBefore := (l3Inch div 8) div 2;
   thePart.rSpaceAfter := (l3Inch div 8) div 2;
  end;//Spaces.Top > 0..
 if (thePart.rDrawType = lpdDraw) AND (anIndex = l3_fpiLeft) then
  thePart.rSpaceBefore := Max(Min(f_FirstIndent, Spaces.Left) - l3Inch div 16, 0);
 if (thePart.rDrawType <> lpdHidden) then
 // - тут прячем лишние рамки (если идёт пачка параграфов с одним стилем)
 begin
  l_Style := ParaX.Attr[k2_tiStyle];
  if l_Style.IsValid then
  begin
   Case anIndex of
    l3_fpiUp:
    begin
     l_N := evPrevOverallPara(ParaX);
     if (l_N <> nil) AND l_N.IsValid AND
      (l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
      l_N.Attr[k2_tiFrame].IsValid then
     thePart.rDrawType := lpdHidden;
    end;//l3_fpiUp
    l3_fpiDown:
    begin
     l_N := evNextOverallPara(ParaX);
     if (l_N <> nil) AND l_N.IsValid AND
      (l_Style.IntA[k2_tiHandle] = l_N.IntA[k2_tiStyle]) AND
      l_N.Attr[k2_tiFrame].IsValid then
     thePart.rDrawType := lpdHidden;
    end;//l3_fpiDown
   end;//Case anIndex
  end;//l_Style.IsValid
 end;//thePart.rDrawType <> lpdHidden
 if (thePart.rDrawType = lpdDraw) then
 begin
  if (anIndex = l3_fpiDown) then
  begin
   thePart.rSpaceBefore := Max(thePart.rSpaceBefore, l3Inch div 16);
   thePart.rSpaceAfter := Max(thePart.rSpaceAfter, l3Inch div 32);
  end;//anIndex = l3_fpiDown
 end;//thePart.rDrawType = lpdDraw
//#UC END# *4804B9BB0383_49DA30D901B6_impl*
end;//TevTextParaPainterEx.GetFramePartPrim

procedure TevTextParaPainterEx.InitBottom(var theBottom: InevBasePoint;
  var theCellBottom: InevBasePoint);
//#UC START# *4804BC800172_49DA30D901B6_var*
var
 l_Pt : TnevPoint;
//#UC END# *4804BC800172_49DA30D901B6_var*
begin
//#UC START# *4804BC800172_49DA30D901B6_impl*
 theCellBottom := nil;
 if (Map <> nil) then
 // - эта проверка НУЖНА, например для вложенных объектов (формул)
  with Map.Bounds do
  begin
   l_Pt := l3Point(Left, Bottom);
   if not DrawCompleted then
   // ^ - http://mdp.garant.ru/pages/viewpage.action?pageId=132220046
    Dec(l_Pt.P.Y, l3Epsilon);
   theBottom.AsLeaf.InitPointByPt(Area.rView.As_InevView, l_Pt, Map);
   if (Bottom > Top + Spaces.Top) then
   // - !!! вместо этой проверки для текстовых параграфов надо использовать
   //       номер строки, т.к. он всё равно известен.
    theBottom := TnevAfterEndPoint.Make(Area.rView.As_InevView, theBottom);
  end;//with Map.Bounds
//#UC END# *4804BC800172_49DA30D901B6_impl*
end;//TevTextParaPainterEx.InitBottom

procedure TevTextParaPainterEx.DoDrawFrameText(aTop: Boolean);
//#UC START# *4804C35B00B2_49DA30D901B6_var*
var
 l_Space : Integer;
 l_Style : InevTag;
 l_Text  : Tl3PCharLen;
 l_Decor : InevTag;
 l_DT    : TnevDecorType;
//#UC END# *4804C35B00B2_49DA30D901B6_var*
begin
//#UC START# *4804C35B00B2_49DA30D901B6_impl*
 f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
 if aTop then
  l_Space := Spaces.Top
 else
  l_Space := Spaces.Bottom;
 if (l_Space > 0) then
 begin
  if (aTop AND
      (f_TopLine = 1)
     ) OR
     
     (not aTop {AND
      (f_TopLine = FormatInfo.Lines.Count) AND}
     ) then
  begin
   if aTop then
    l_DT := nev_dtHeader
   else
    l_DT := nev_dtFooter;
   l_Decor := FormatInfo.DecorObj(l_DT);
   l_Style := ParaX.Attr[k2_tiStyle];
   if l_Style.IsValid then
   begin
    l_Text := l_Style.PCharLenA[k2_tiShortName];
    if not l3IsNil(l_Text) then
     InternalDrawFrameText(l_Text, aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
    else
    if l_Decor.IsValid then
     InternalDrawFrameText(Tl3PCharLen(cc_EmptyStr), aTop, l_Space, l_DT, l_Decor, f_FirstIndent)
   end;//l_Style.IsValid
  end;//aTop
 end;//l_Space > 0
//#UC END# *4804C35B00B2_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DoDrawFrameText

function TevTextParaPainterEx.DrawLeaf: Boolean;
//#UC START# *4804C81000B9_49DA30D901B6_var*
var
 LH              : Integer; {- высота i-й строки}
 l_Justification : TevIndentType; {- тип выравнивания параграфа}
 l_ParaFont      : Il3Font;
 l_ParaS         : Tl3PCharLen;
 l_IsHyperlink   : Boolean;

 procedure DrawIndent(First     : Boolean;
                      DX        : Integer;
                      LL        : Boolean;
                      AddHyphen : Boolean);
 var
  D              : Integer;
  HardEnterWidth : Integer;
  l_PMStr        : Tl3PCharLenPrim;
 begin//DrawIndent
  with Area.rCanvas do
  begin
   if not First AND AddHyphen then
    ExtTextOut(l3Point0,
                      l3Rect(0, 0, evHyphenWidth, LH),
                      cc_HyphenStr);
   if ((DX > 0) AND (l_Justification <> ev_itWidth)) then
   begin
    if ((First AND (l_Justification in [ev_itRight, ev_itCenter])) OR
       (not First AND
        ((l_Justification in [ev_itLeft, ev_itCenter]) OR DrawSpecial))) then
    begin
     D := 0;
     case l_Justification of
      ev_itLeft:
       if not First then
        D := DX;
      ev_itRight:
       Inc(D, DX);
      ev_itCenter:
       if First then
        Inc(D, DX div 2)
       else
        Inc(D, DX - (DX div 2));
      ev_itWidth: {D уже равен X0};
     end;//case l_Justification
     if not First AND DrawSpecial AND LL then
     begin
      if (ParaMarkStyle = ev_pmCell) then
       l_PMStr := cc_CurrencyStr
      else
       l_PMStr := cc_HardEnterStr;
      if l3IsNil(l_ParaS) then
      begin
       Font := l_ParaFont;                                                 
       ValidateParaFont;
      end;//l3IsNil(l_ParaS)
      PushFC;
      try
       with Font do
       begin
        Name := def_ArialFontName;
        Style := [];
       end;//with Font
      finally
       PopFC;
      end;//try..finally
      HardEnterWidth := TextExtent(l_PMStr).X;
      if (HardEnterWidth > D) then
       D := HardEnterWidth;
      if (SelRange <> nil) AND SelRange.ContainsEnd(Area.rView.As_InevView) then
      begin
       BeginInvert;
       try
        ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
       finally
        EndInvert;
       end;//try..finally
      end//(SelRange <> nil) AND SelRange.ContainsEnd
      else
       ExtTextOut(l3Point0, l3Rect(0, 0, HardEnterWidth, LH), l_PMStr);
     end;//not First
     MoveWindowOrg(l3PointX(-D));
     if l_IsHyperlink then DrawArrow(LH);
    end;//First...
   end;//AddHyphen
  end;//with Area.rCanvas
 end;//DrawIndent

var
 WC            : Integer;   {- количество слов в i-й строке}
 DW            : Integer;
 ParaCaretPos  : Integer;
 LineCaretPos  : Integer;
 ParaCaretLine : Integer;
 l_NeedCaret   : Boolean;
 l_Map         : InevLines;
 l_SolidBlock  : Boolean;

 function loc_DrawText(S: Tl3CustomString): Boolean;

  function _TextRect(const S: Tl3PCharLen): Boolean;

   procedure DrawSoftEnter;
   begin//DrawSoftEnter
    with Area.rCanvas do
    begin
     PushFC;
     try
      with Font do
      begin
       Name := def_SymbolFontName;
       Style := [];
      end;//with Font
     finally
      PopFC;
     end;//try..finally
     CaretLineOut(cc_SoftEnterStr, LH, not l_NeedCaret, LineCaretPos);
    end;//with Area.rCanvas
   end;//DrawSoftEnter

  var
   TLen            : Integer;
   l_NeedSoftEnter : Boolean;
   S2Draw          : Tl3PCharLen;
   l_Str           : Tl3PCharLen;
  begin
   with Area.rCanvas do
   begin
    CheckInvertInLeftAAC;
    Result := ((ClipRect.Right > 0) OR (Caret <> nil)) AND HasToDraw;
    if Result then
    begin
     S2Draw := S;
     TLen := l3RTrim(S2Draw).SLen;
     if (TLen > 0) AND (S2Draw.S[Pred(TLen)] = cc_SoftEnter) then
     begin
      l_NeedSoftEnter := DrawSpecial;
      if l_NeedSoftEnter OR (LineCaretPos < 0) OR (LineCaretPos > S2Draw.SLen) then
       Dec(TLen)
      else
      begin
       {-эта ветка необходима для правильного позиционирования курсора
           на конец строки с SoftEnter'ом }
       S2Draw := CheckOutString(S2Draw);
       S2Draw.S[Pred(TLen)] := cc_HardSpace;
       TLen := S2Draw.SLen;
      end;//l_NeedSoftEnter
     end//TLen > 0
     else
     begin
      l_NeedSoftEnter := false;
      TLen := S2Draw.SLen;
     end;//TLen > 0
     l_Str := l3PCharLen(S2Draw.S, TLen, S2Draw.SCodePage);
     if DrawSpecial then
      l_Str := CheckConvertString(l_Str);
     CaretLineOut(l_Str, LH, not l_NeedCaret, LineCaretPos);
     if l_NeedSoftEnter then
      DrawSoftEnter;
    end;//Result
   end;//with Area.rCanvas
  end;//_TextRect

  function DrawAllLine: Boolean;
    {-нарисовать всю строку, без выделения}
  begin//DrawAllLine
   Result := _TextRect(S.AsPCharLen);
   S.Offset(S.Len);
  end;//DrawAllLine

 var
  D2S : Integer; {-ширина пустого места}

  procedure GetD2S;
  begin//GetD2S
   if (WC > 0) then
   begin
    {if (DW < 0) then begin
     D2S := 0;
     DW := 0;
    end else }begin
     if (WC = 1) then
      D2S := DW
     else
      D2S := DW div WC;
     Dec(WC);
     with Area.rCanvas do
     begin
      D2S := LP2DP(l3PointX(D2S)).X;
      Dec(DW, DP2LP(PointX(D2S)).X);
     end;//with Area.rCanvas
    end;//DW < 0
   end
   else
    D2S := 0;
  end;//GetD2S

 var
  l_InDrawBlock      : Boolean;
  l_NeedCorrectCaret : Boolean;
  l_pxLH             : Integer;
  l_NS               : Tl3CustomString;
 begin//loc_DrawText
  Result := true;
  with Area.rCanvas do
  begin
   if S.Empty then
   begin
    if (LineCaretPos = 0) then
    begin
     SetCaret(l3Point0, l3Point(AverageCharWidth, LH), not l_NeedCaret);
     ParaCaretPos := -1;
     LineCaretPos := -1;
     ShowCursor := false;
    end;//LineCaretPos = 0
   end//S.Empty
   else
   begin
    if (l_Justification <> ev_itWidth) OR
       not l3HasWhiteSpace(S.AsPCharLen) then
    begin
     {-проверяем нужна ли разгонка и есть ли в строке пробелы}
     if l_SolidBlock then
      Result := DrawAllLine
     else
      f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S);
    end//l_Justification <> ev_itWidth
    else
    begin
     {-рисуем разогнанную строку}
     l_pxLH := LP2DP(l3PointY(LH)).Y;
     l_NS := S.Clone;
     try
      while Result AND not S.Empty do
      begin
       S.FindCharEx(cc_HardSpace, l_NS);
       l_NeedCorrectCaret := (LineCaretPos = S.Len);
       if l_SolidBlock then
       begin
        Result := DrawAllLine;
        l_InDrawBlock := false;
       end//l_SolidBlock
       else
        l_InDrawBlock := Boolean(f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@_TextRect), S));
       if Result AND l3IsWhiteSpaceS(S.AsPCharLen, -1) then
       begin
        if l_InDrawBlock then
        begin
         BeginInvert;
         CheckInvertInLeftAAC;
        end;
        try
         GetD2S;
         FillRect(l3SRect(0, 0, D2S, l_pxLH));
         MoveWindowOrg(PointX(-D2S));{-сдвигаем курсор отрисовки на ширину пустого места}
         if l_NeedCorrectCaret then
          IncCaret(D2S - 1);
         Result := (ClipRect.Right > 0) AND HasToDraw;
        finally
         if l_InDrawBlock then
          EndInvert;
        end;//try..finally
       end;//Result AND evWhiteSpace
       S.AssignString(l_NS);
      end;//while Result AND not S.Empty
     finally
      l3Free(l_NS);
     end;//try..finally
    end;//l_Justification <> ev_itWidth
   end;//S.Empty
   Result := Result AND (ClipRect.Right > 0) AND HasToDraw;
  end;//with Area.rCanvas
 end;//loc_DrawText

var
 FirstLineWidth : Integer;
 OtherLineWidth : Integer;
 l_LineOffsetX  : Integer;
 l_ExtentX      : Integer;
 l_LineCount    : Integer;
 l_ActiveElement : InevActiveElement;

 procedure DrawPara;

  function DrawLine(ppLI: PPevLineInfo; LID: Integer): Boolean;
  var
   l_SegmentsList : Ik2TagList;

   function DrawSegment(const aSeg: InevTag; Index: Integer): Boolean;

   var
    l_OM : TOutlineTextMetric;
    l_Pnt : IevPainter;
    l_DrawingObject : InevTag;
    
    function DrawObject(const S: Tl3PCharLen): Boolean;
    var
     l_Ex     : Tl3Point;
     l_Obj    : InevObject;
     l_SegInf : TnevShapeInfo;
     l_Points : TnevShapePoints;
     l_Point  : InevPoint;
     l_BL     : Integer;
    begin//DrawObject
     Result := true;
     with Area.rCanvas do
     begin
      l_Ex := TextExtent(S);
      PushWO;
      try
       l_BL := aSeg.IntA[k2_tiBaseLine];
       if not l_DrawingObject.InheritsFrom(k2_idBitmapPara) then
       // http://mdp.garant.ru/pages/viewpage.action?pageId=174295160&focusedCommentId=344137422#comment-344137422
       // - пока так
        MoveWindowOrg(l3PointY(-((LH - l_Ex.Y) div 2)))
       else
       begin
 {       if (l_OM.otmDescent <> 0) then
         if (l_OM.otmDescent <> 1) then
          l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent - 1)).Y;}
        if (l_OM.otmDescent <> 0) then
          l_BL := l_BL + Area.rCanvas.DP2LP(PointY(l_OM.otmDescent)).Y;
        MoveWindowOrg(l3PointY(-((LH - l_Ex.Y))));
       end;//not l_DrawingObject.InheritsFrom(k2_idBitmapPara)
       if (l_BL <> 0) then
        MoveWindowOrg(l3PointY(-l_BL));
       l3FillChar(l_Points, SizeOf(l_Points));
       l_Points.rTop := nil;
       l_Points.rCaret := nil;
       l_Points.rSelection := nil;
       l_Points.rPrevHeight := 0;
       l_Points.rRealTop := nil;
       l_Points.rFake := False;
       l_Points.rCheckCaret := nil;
       l_Points.rCheckSelection := nil;
       if (LineCaretPos > 0) then
        Dec(LineCaretPos, S.SLen);
       if (SelRange <> nil) then
       begin
        l_Point := ParaX.MakePoint;
        l_Point.PositionW := aSeg.IntA[k2_tiStart];
        if SelRange.Contains(Area.rView.As_InevView, l_Point) then
         if l_DrawingObject.QT(InevObject, l_Obj) then
          l_Points.rSelection := l_Obj.Range;
       end;//SelRange <> nil
 (*           if (LineCaretPos = 0) AND QT(InevObject, l_Obj) then
        l_Pnt.Draw(nil, Area.rCanvas, nil, l_Obj.Point, nil, l_SegMap, High(Integer))
       else*)
        l_Pnt.Draw(Area^, l_Points, l_SegInf);
      finally
       PopWO;
      end;//try..finally
      MoveWindowOrg(l3PointX(-l_Ex.X));
      MoveWindowOrg(PointX(-1));
     end;//with Area.rCanvas
    end;//DrawObject

   var
    l_SegFont  : Il3Font;
    l_BC       : Tl3Color;
    l_ActiveHyperlinkStyle : InevTag;
    l_AH                   : InevActiveElement;
    l_CheckedS1 : Tl3CustomString; 
    l_CheckedS2 : Tl3CustomString;
    l_CheckedS3 : Tl3CustomString;
   begin//DrawSegment
    l_CheckedS1 := CheckS(1);
    l_CheckedS2 := CheckS(2);
    l_CheckedS3 := CheckS(3);
    evTextParaGetLineSegment(l_Map, l_ParaS, Succ(LID), Index, l_SegmentsList,
                             l_CheckedS1, l_CheckedS2, l_CheckedS3
                             {CheckS(1), CheckS(2), CheckS(3)});
    Result := loc_DrawText(l_CheckedS1{S[1]});
    if Result then
    begin
     with Area.rCanvas do
     begin
      Push;
      try
       l_SegFont := FormatInfo.InfoForChild(TnevSegmentObject.Make(aSeg)).Font[false];
       //l_SegFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, aSeg, true, FormatInfo^, false);
       if l_SegFont.IsAtomic then
       begin
        l3FillChar(l_OM, SizeOf(l_OM));
        if (GetOutlineTextMetrics(Area.rCanvas.DC, SizeOf(l_OM), @l_OM) = 0) then
         Assert(false, 'Не удалось получить метрики текущего шрифта канвы');
       end;//l_SegFont.IsAtomic
       l_BC := BackColor;
       Font := l_SegFont;
       // - устанавливаем шрифт сегмента
       if (l_ActiveElement <> nil) then
       begin
        l_AH := TnevActiveHyperlink.Make(ParaX, aSeg);
        if ParaX.IsDecorationElement then
         l_AH := TnevDecorActiveHyperlink.Make(ParentPainter.Obj, l_AH);
        //if aSeg.InheritsFrom(k2_idHyperlink) then // - это убрано, чтобы корректно продолжения ссылок подчёркивались
         if l_ActiveElement.IsSame(l_AH) then
         begin
          l_ActiveHyperlinkStyle := k2.TypeTable.ObjToTag(k2.TypeTable[k2_idTextStyle].ValueTable.DRByID[evd_saActiveHyperLink]);
          if l_ActiveHyperlinkStyle.IsValid then
          begin
           if l_ActiveHyperlinkStyle.HasSubAtom(k2_tiFont) then
           begin
            with l_ActiveHyperlinkStyle.Attr[k2_tiFont] do
            begin
             with Font do
             begin
              if HasSubAtom(k2_tiUnderline) then
               Underline := BoolA[k2_tiUnderline];
              if HasSubAtom(k2_tiBold) then
               Bold := BoolA[k2_tiBold];
              if HasSubAtom(k2_tiItalic) then
               Italic := BoolA[k2_tiItalic];
              if HasSubAtom(k2_tiStrikeout) then
               Strikeout := BoolA[k2_tiStrikeout];
              if HasSubAtom(k2_tiForeColor) then
               ForeColor := IntA[k2_tiForeColor];
              if HasSubAtom(k2_tiBackColor) then
               BackColor := IntA[k2_tiBackColor];
             end;//Font
            end;//with l_ActiveHyperlinkStyle.Attr[k2_tiFont]
           end;//l_ActiveHyperlinkStyle.HasSybAtom(k2_iFont)
          end;//l_ActiveHyperlinkStyle.IsValid
         end;//l_ActiveElement.IsSame(TnevActiveHyperlink.Make(ParaX, aSeg))
       end;//l_ActiveElement <> nil
       if (l_SegFont.BackColor = nevDefaultColor) AND
          aSeg.HasSubAtom(k2_tiVisible) then
        if aSeg.BoolA[k2_tiVisible] then
         if not ParaX.BoolA[k2_tiVisible] then
         // - принудительно выставляем цвет фона для видимых сегментов
          BackColor := l_BC;
       ValidateSegFont(aSeg);
       if l_SegFont.IsAtomic then
       begin
        l_DrawingObject := aSeg.Child[0];
        if l_DrawingObject.QT(IevPainter, l_Pnt) then
         try
          l_Pnt.ParentPainter := Self;
          f_LinePainter.DrawF(As_Il3Canvas, evL2DLA(@DrawObject), l_CheckedS2{S[2]});
          Result := true;
         finally
          l_Pnt := nil;
         end//try..finally
        else
         Result := loc_DrawText(l_CheckedS2{S[2]});
       end//l_SegFont.IsAtomic
       else
        Result := loc_DrawText(l_CheckedS2{S[2]});
      finally
       Pop;
      end;//try..finally
      if Result then
      begin
       Font := l_ParaFont;
       // - возвращаем шрифт параграфа
       ValidateParaFont;
       Result := loc_DrawText(l_CheckedS3{S[3]});
      end;//Result
     end;//with Area.rCanvas
    end;//Result
   end;//DrawSegment
                                   
   procedure DrawBullet;
   const
    cc_BulletStr : Tl3PCharLenConst = (S         : #$B7;
                                       SLen      : 1;
                                       SCodePage : CP_ANSI);
   var
    l_BulletWidth : Integer;
    l_Bullet      : Integer;
   begin//DrawBullet
    l_Bullet := ParaX.IntA[k2_tiBullet];
    if (l_Bullet > 0) then
    begin
     // - bullet присутствует
     with Area.rCanvas do
     begin
      Font.Name := def_SymbolFontName;
      l_BulletWidth := TextExtent(cc_BulletStr).X;
      MoveWindowOrg(l3PointX(2 * l_BulletWidth));
      ExtTextOut(l3Point0, l3Rect(0, 0, l_BulletWidth, LH), cc_BulletStr);
      MoveWindowOrg(l3PointX(-2 * l_BulletWidth));
      Font := l_ParaFont;
      //- восстанавливаем шрифт параграфа
      ValidateParaFont;
     end;//with Area.rCanvas
    end;//l_Bullet > 0
   end;//DrawBullet
   
  var
   LeftIndent   : Integer;
   MaxLineWidth : Integer;
   IsLastLine   : Boolean;
   l_LineLength : Integer;
   l_TabInfo    : Il3TabInfo;
   l_SaveOrg    : Tl3Point;
   l_TabIndent  : Integer;
   l_CheckedS1  : Tl3CustomString;
  begin//DrawLine
   Result := true;
   l_TabIndent := 0;
   with Area.rCanvas do
   begin
    StartTabs(l_TabInfo, ParaX.TabStops);
    try
     try
      if (l_LineOffsetX >= 0) then
      begin
       l_SaveOrg := WindowOrg;
       try
        LH := ppLI^^.LE.Y;
        Dec(l_SaveOrg.P.Y, LH);
        {-сдвигаем курсор отрисовки на высоту строки}
        l_LineLength := ppLI^^.B - l_LineOffsetX;
        {-вычисляем длину строки}
        if ShowCursor AND (ParaCaretPos >= l_LineOffsetX) AND
           (LID = ParaCaretLine) then
         LineCaretPos := ParaCaretPos - l_LineOffsetX
        else
         LineCaretPos := -1;
        if (ClipRect.Top >= LH) and ((Caret = nil) or not ShowCursor) then
         Exit;
        if not DrawRgnOrBlock AND
           ((LineCaretPos < 0) OR (LineCaretPos > l_LineLength)) then
         Exit;
        f_LinePainter.StartLine(l_LineOffsetX, l_LineLength);
        Font := l_ParaFont;
        if (LID > 0) then
        begin
         MaxLineWidth := OtherLineWidth;
         LeftIndent := Spaces.Left;
        end//LID > 0
        else
        begin
         MaxLineWidth := FirstLineWidth;
         LeftIndent := f_FirstIndent;
         if (ParentPainter <> nil) and
            (ParentPainter.ParentPainter <> nil) and
            (ParentPainter.ParentPainter.TableRowPainter <> nil) then
         begin
          l_TabIndent := evCalcDecimalTabIndent(ParaX, l_ParaS, l_Justification, l_LineCount, As_Il3Canvas);
          Inc(LeftIndent, l_TabIndent);
         end; // if (ParaMarkStyle = ev_pmCell) and (ParaX.TabStops <> nil) then
        end;//(LID > 0)
        IsLastLine := (LID = l_LineCount);
        if IsLastLine AND (l_Justification = ev_itWidth) then
         l_Justification := ev_itLeft;
        FontIndexSet := ppLI^^.FI;
        WC := ppLI^^.WC;
        if Printing then
        begin
         if (ClipRect.Bottom - LH) <= 5 * l3Epsilon then
         // - не нужно ли начать новую страницу
         begin
          Result := false;
          // - не нужно оставшиеся строки печатать
          DrawLeaf := false;
          // -недопечатали...
          Inc(l_SaveOrg.P.Y, LH);
          // - учитываем недопечатанную строчку
          Exit;
          // -выходим
         end;//ClipRect.Bottom < LH
        end;//Printing
        DW := MaxLineWidth - ppLI^^.LE.X - l_TabIndent;
        Assert((l_Justification = ev_itPreformatted) OR (DW < High(Integer) div 4), 'Значение ширины похоже на мусор');
        l_SegmentsList := evTextParaGetLineSegments(FormatInfo, l_ParaS, ParaX, Succ(LID));
        MoveWindowOrg(l3PointX(-LeftIndent));
        // - сдвигаем курсор отрисовки на ширину отступа
        DrawIndent(true, DW, IsLastLine, false);
        // - рисуем левый отступ
        //Font := l_ParaFont;
        // - устанавливаем шрифт параграфа
        ValidateParaFont;
        if (LID = 0) then
         DrawBullet;
         // - рисуем маркер списка
        if l_SegmentsList.Empty then
        begin
         l_CheckedS1 := CheckS(1);
         with l_CheckedS1 do
          if (l_ParaS.S = nil) then
           Clear
          else
           AsPCharLen := l3PCharLenPart(l_ParaS.S,
                                        l_LineOffsetX, l_LineOffsetX + l_LineLength,
                                        l_ParaS.SCodePage);
         loc_DrawText(l_CheckedS1{S[1]});
        end//l_SegmentsList.Empty
        else
        begin
         CheckDrawFocused;
         l_SegmentsList.ForEachF(k2L2TIA(@DrawSegment));
        end;
        DrawIndent(false, DW, IsLastLine, ppLI^^.AddHyphen);
        {-рисуем правый отступ и символ конца параграфа}
       finally
        WindowOrg := l_SaveOrg;
        if (ClipRect.Bottom <= 0) then
         Result := false;
        // -если уперлись вниз экрана то ВЫХОД
        Result := Result AND HasToDraw;
        // - еще осталось что рисовать
       end;//try..finally
      end;//l_LineOffsetX >= 0
     finally
      l_LineOffsetX := ppLI^^.B;
      // - запоминаем конец текущей строки
     end;//try..finally
    finally
     FinishTabs(l_TabInfo);
    end;//try..finally
   end;//with Area.rCanvas
  end;//DrawLine

 var
  IA : Tl3IteratorAction;
 begin//DrawPara
  l_Justification := TevIndentType(ParaX.IntA[k2_tiJustification]);
  with Area.rCanvas do
  begin
   if not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX] then
   begin
    if (l_Justification = ev_itWidth) AND IsVirtual then
     l_Justification := ev_itLeft;
    f_FirstIndent := FormatInfo.FirstIndent;
    l_LineCount := Pred(evTextParaLineCount(FormatInfo));
    FirstLineWidth := l_ExtentX - f_FirstIndent;
    OtherLineWidth := l_ExtentX - Spaces.Left;
    Push;
    try
     l_ParaFont := FormatInfo.Font[false];
     //l_ParaFont := ETAOIN_SHRDLU_GetObjIFont(Area.rView.Metrics, ParaX, false, FormatInfo^, false);
     // -вычисляем шрифт параграфа
     try
      IA := l3L2IA(@DrawLine);
      try
       if (l_LineOffsetX >= 0) then
        evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, Pred(f_TopLine)))
       else
        evTextParaIterateLines(FormatInfo, l_ParaS, ParaX, IA, Max(0, f_TopLine - 2));
      finally
       l3FreeIA(IA);
      end;//try..finally
     finally
      l_ParaFont := nil;
     end;//try..finally
    finally
     Pop;
    end;//try..finally
   end//not ParaX.TreatCollapsedAsHidden OR not Area.rView.IsObjectCollapsed[ParaX]
   else
    Result := true;
   if Result then
    MoveWindowOrg(l3PointY(-Spaces.Bottom));
    // - учитываем отступ после абзаца
  end;//with Area.rCanvas
 end;//DrawPara

//#UC END# *4804C81000B9_49DA30D901B6_var*
begin
//#UC START# *4804C81000B9_49DA30D901B6_impl*
 StartDrawingInitFields;
 f_HeaderHeight := 0;
 l_IsHyperlink := EvIsParaHyperlink(ParaX);
 Result := true;
 l_ActiveElement := Area.rView.ActiveElement;
 l_Map := FormatInfo.Lines;
 l_ExtentX := Max(FormatInfo.rLimitWidth - Spaces.Right, FormatInfo.Width);
 f_TopLine := Succ(TopAnchor.VertPosition(Area.rView.As_InevView, FormatInfo));
 if (Caret <> nil) AND Area.rView.ForceDrawFocusRect then
  f_ForceFocusRect := true;
 with Area.rCanvas do
 begin
  PushWO;
  try
   if Result AND (f_TopLine = 1) then
    MoveWindowOrg(l3PointY(-Spaces.Top));
    // - учитываем отступ перед абзацем
   if not Printing OR (ClipRect.Bottom > 0) then
   begin
    l_ParaS := ParaX.Text;
    if ShowCursor AND (Caret <> nil) then
    begin
     l_NeedCaret := Caret.NeedWindowsCaret;
     l_LineOffsetX := evTextParaGetPosByLine(l_Map, l_ParaS, f_TopLine);
     ParaCaretPos := Caret.Position;
     ParaCaretLine := Caret.VertPosition(Area.rView.As_InevView, FormatInfo);
    end//ShowCursor
    else
    begin
     l_NeedCaret := false;
     if (f_TopLine = 1) then
      l_LineOffsetX := 0
     else
      l_LineOffsetX := -1;
     ParaCaretPos := -1;
    end;//ShowCursor
    if (f_LinePainter = nil) then
     f_LinePainter := TevLinePainter.Create;
    f_LinePainter.StartPara(Area.rView.As_InevView, SelRange);
    try
     l_SolidBlock := (SelRange = nil);
     if l_SolidBlock then
      DrawPara
     else
     begin
      l_SolidBlock := SelRange.Solid(Area.rView.As_InevView);
      if l_SolidBlock then
      begin
       if (l_ParaS.S = nil) then
        DrawPara
       else
       begin
        BeginInvert;
        try
         DrawPara;
        finally
         EndInvert;
        end;{try..finally}
       end;//l_ParaS.S = nil
      end//l_SolidBlock
      else
       DrawPara;
     end;//l_SolidBlock
    finally
     f_LinePainter.FinishPara;
    end;//try..finally
   end;//not Printing OR (ClipRect.Bottom >= 0
  finally
   PopWO;
  end;//try..finally
 end;//with Area.rCanvas
//#UC END# *4804C81000B9_49DA30D901B6_impl*
end;//TevTextParaPainterEx.DrawLeaf

procedure TevTextParaPainterEx.FillUnfilled(const aRect: Tl3Rect);
//#UC START# *4E2702FE01EA_49DA30D901B6_var*
const
 cRad = 12;
 cArrow = 10;
 cFW = 1;
var
 l_Rgn  : Tl3Region;
 l_Rgn1 : Tl3Region;
 l_Rgn2 : Il3Region;
 l_R    : Tl3Rect;
 l_SR   : Tl3SRect;
 l_PointArray : array of TPoint;
 l_Med : Integer;
 //l_BC : TColor;
 l_NeedTriangle : Boolean;
//#UC END# *4E2702FE01EA_49DA30D901B6_var*
begin
//#UC START# *4E2702FE01EA_49DA30D901B6_impl*
 if ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable] AND
    (
     (ParaX.BackColor <> nevDefaultColor)
(*     (ParaX.IntA[k2_tiBackColor] <> nevDefaultColor) OR
     (ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor] <> nevDefaultColor)*)
    ) AND
    not Area.rView.IsObjectCollapsed[ParaX] AND
    Area.rCanvas.DrawEnabled and not Area.rCanvas.IsVirtual then
 begin
(*  l_BC := ParaX.IntA[k2_tiBackColor];
  if (l_BC = nevDefaultColor) then
  begin
   l_BC := ParaX.Attr[k2_tiStyle].Attr[k2_tiFont].IntA[k2_tiBackColor];
   if (l_BC = nevDefaultColor) then
   begin
    inherited;
    Exit;
   end;//l_BC = nevDefaultColor
  end;//l_BC = nevDefaultColor*)
  with Area.rCanvas do
  begin
   l_Rgn := Tl3Region.Create;
   try
    PushClipRect;
    try
     l_R := aRect;
     if (f_TopLine = 1) then
      l_R.Top := l_R.Top + f_HeaderHeight
     else
      l_R.Top := l_R.Top - f_HeaderHeight;
     l_SR := LR2DR(l_R);
     with l_SR do
      l_Rgn.Rgn := CreateRoundRectRgn(Left, Top, Right, Bottom, cRad, cRad);
     ClipRegion := l_Rgn;
     inherited;
    finally
     PopClipRect;
    end;//try..finally
    PushClipRect;
    try
     l_Rgn1 := Tl3Region.Create;
     try
      l_NeedTriangle := (f_TopLine = 1) AND (f_HeaderHeight <> 0);
      if l_NeedTriangle then
      begin
       SetLength(l_PointArray, 3);
       l_Med := l_SR.Left + (l_SR.Right - l_SR.Left) div 2;
       l_PointArray[0] := Point(l_Med - cArrow, l_SR.Top);
       l_PointArray[1] := Point(l_Med + cArrow, l_SR.Top);
       l_PointArray[2] := Point(l_Med, l_SR.Top - cArrow);
       l_Rgn1.Rgn := Windows.CreatePolygonRgn(l_PointArray[0],
                                              Length(l_PointArray),
                                              ALTERNATE{WINDING});
       ClipRegion := l_Rgn1;
       FillRgn(l_Rgn1);
      end;//f_TopLine = 1
      l_Rgn2 := l_Rgn.Clone;
      if l_NeedTriangle then
       l_Rgn2.Combine(l_Rgn1, RGN_OR);
      ClipRegion := l_Rgn2;
      Push;
      try
       Canvas.Brush.Color := clSilver;
       FrameRgn(DC, l_Rgn2.Rgn, Canvas.Brush.Handle, cFW, cFW);
      finally
       Pop;
      end;//try..finally
     finally
      FreeAndNil(l_Rgn1);
     end;//try..finally
    finally
     PopClipRect;
    end;//try..finally
    PushClipRect;
    try
     BackColor := TopBC;
     //BackColor := l_BC;
     l_Rgn1 := Tl3Region.Create;
     try
      l_Rgn1.Rect := LR2DR(l_R);
      l_Rgn1.Combine(l_Rgn, RGN_DIFF);
      ClipRegion := l_Rgn1;
      FillRect(l_R);
     finally
      FreeAndNil(l_Rgn1);
     end;//try..finally
    finally
     PopClipRect;
    end;//try..finally
   finally
    FreeAndNil(l_Rgn);
   end;//try..finally
  end;//with Area.rCanvas
 end//ParaX.Attr[k2_tiStyle].BoolA[k2_tiCollapsable]
 else
  if not SpecialFill(aRect, False) then
   inherited;
//#UC END# *4E2702FE01EA_49DA30D901B6_impl*

end;//TevTextParaPainterEx.FillUnfilled

9 комментариев:

  1. Да, интересен, конечно.
    Пару лет назад мне почему-то думалось, что разработка компонентов для FM - хорошая идея. В том числе с коммерческой точки зрения.
    Тоже хотел сделать аналог TRichEdit (да, тоже есть наработки в данной области).
    Чтобы с этим делом не попасть, даже спросил у Леонова в блоге, не собирается ли EMBT сама такое делать (было бы логично).
    Разумеется, внятного ответа не получил. И забил. В общем-то сейчас даже благодарен ему за это.:)

    ОтветитьУдалить
    Ответы
    1. Внятный ответ:
      "Да, братуха! ты поднимешь миллион!"
      Оно?

      >>что разработка компонентов для FM - хорошая идея
      Это кто умеет разрабатывать компоненты.
      TMS Software, например.

      - Является программирование хорошей идеей?
      - Да, для тех, кто умеет программировать. Для остальных - плохая идея.

      Удалить
    2. > "Да, братуха! ты поднимешь миллион!"
      > Оно?

      Ага, братуха, оно!11
      Самый что ни на есть внятный ответ на вопрос "не собирается ли EMBT сама такое делать?"!11

      > Это кто умеет разрабатывать компоненты.
      Ну вот, а нельзя на сайте прямо написать, что компоненты для FMX можно делать только тем, кто умеет?
      А то я чуть было не полез со своим свиным рылом, да в калашный ряд.

      Удалить
  2. " В общем-то сейчас даже благодарен ему за это.:)"

    -- я вот пока - так же думаю.

    ОтветитьУдалить
  3. Кода дофига, наверное даже важного и нужного. Уж извините за следующее..
    Но такое уродское именование всего и вся просто убивает желание его читать/понимать.

    ОтветитьУдалить
    Ответы
    1. Поподробнее можно? Я ведь тоже могу сказать - "код - говно"... А детали?

      Удалить
    2. Хм... А где тут уродское именование? GetViewSegmentsPlace? DrawAllLine? l_LineOffsetX?
      Хотите примеров уродского именования - пожалуйста:
      select ecfil139.DATA as myDate, (0 - ecfil139.SUMMA_ZAPROSHENAYA_CHEM) as summa
      from ecfil139 where ID_KLIENTA = 314 and ID_KOSH_ZA_CHTO = 6
      UNION all
      select ecfil096.DATA as myDate, ecfil096.SUMMA as summa from ecfil096 where ID_VLAD = 314
      order by myDate ASC

      Удалить