MarkUp.pas
上传用户:yj_qiu
上传日期:2022-08-08
资源大小:23636k
文件大小:10k
源码类别:

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(Text markup unit)
  3.  (C) 2006 George "Mirage" Bakhtadze. <a href="http://www.casteng.com">www.casteng.com</a> <br>
  4.  The source code may be used under either MPL 1.1 or LGPL 2.1 license. See included license.txt file <br>
  5.  Unit contains text markup base class and simple markup language implementation class
  6. *)
  7. {$Include GDefines.inc}
  8. unit Markup;
  9. interface
  10. uses TextFile, BaseTypes, Basics, BaseGraph;
  11. const
  12. // Align modes
  13.   amLeft = 0; amCenter = 1; amRight = 2; amJustify = 3;
  14. type
  15.   TName = string[128];
  16.   TTag = class
  17.     Position: Integer;
  18.     constructor Create(APosition: Integer);
  19.   end;
  20.   TColorTag = class(TTag)
  21.     Color: BaseTypes.TColor;
  22.     constructor Create(APosition: Integer; AColor: BaseTypes.TColor);
  23.   end;
  24.   TAlphaColorTag = class(TColorTag)
  25.   end;
  26.   TColorResetTag = class(TTag)
  27.   end;
  28.   TFontStyleTag = class(TTag)
  29.     Kind, Amount: Word;
  30.     constructor Create(APosition: Integer; AKind, AAmount: Word);
  31.   end;
  32.   TIndentTag = class(TTag)
  33.     Amount: Integer;
  34.     constructor Create(APosition: Integer; AAmount: Integer);
  35.   end;
  36.   TReturnTag = class(TTag)
  37.   end;
  38.   TMoveToTag = class(TTag)
  39.     X, Y: Single;
  40.     constructor Create(APosition: Integer; AX, AY: Single);
  41.   end;
  42.   TAlignTag = class(TTag)
  43.     Align: Integer;
  44.     constructor Create(APosition: Integer; AAlign: Integer);
  45.   end;
  46.   TLinkTag = class(TTag)
  47.     Name: TName;
  48.     constructor Create(APosition: Integer; const AName: string);
  49.   end;
  50.   TTags = array of TTag;
  51.   TMarkup = class
  52.   protected
  53.     FPureText, FText: string;
  54.     FTotalTags: Integer;
  55.     procedure ParseFormatting; virtual;
  56.   private
  57.     Parsed: Boolean;
  58.     FTags: TTags;
  59.     procedure AddTag(const Tag: TTag);
  60.     procedure InsertTag(Pos: Integer; const Tag: TTag);
  61.     procedure SetFText(const Value: string); virtual;
  62.     function GetPureText: string;
  63.     function GetTag(Index: Integer): TTag;
  64.   public
  65.     DefaultFont: TFont;
  66.     DefaultWidth: Single;
  67.     SeparatorChars, InvisibleChars: string;
  68.     constructor Create;
  69.     destructor Destroy; override;
  70.     procedure Invalidate;
  71.     property FormattedText: string read FText write SetFText;
  72.     property PureText: string read GetPureText;
  73.     property Tags[Index: Integer]: TTag read GetTag;
  74.     property TotalTags: Integer read FTotalTags;
  75.   end;
  76.   TSimpleMarkup = class(TMarkup)
  77.   protected
  78.     procedure ParseFormatting; override;
  79.   end;
  80. implementation
  81. uses SysUtils;
  82. { TTag }
  83. constructor TTag.Create(APosition: Integer);
  84. begin
  85.   Assert(APosition >= 0, ClassName + '.Create: Position is negative');
  86.   Position := APosition;
  87. end;
  88. { TColorTag }
  89. constructor TColorTag.Create(APosition: Integer; AColor: BaseTypes.TColor);
  90. begin
  91.   inherited Create(APosition);
  92.   Color := AColor;
  93. end;
  94. { TFontStyleTag }
  95. constructor TFontStyleTag.Create(APosition: Integer; AKind, AAmount: Word);
  96. begin
  97.   inherited Create(APosition);
  98.   Kind := AKind; Amount := AAmount;
  99. end;
  100. { TIndentTag }
  101. constructor TIndentTag.Create(APosition: Integer; AAmount: Integer);
  102. begin
  103.   inherited Create(APosition);
  104.   Amount := AAmount;
  105. end;
  106. { TMoveToTag }
  107. constructor TMoveToTag.Create(APosition: Integer; AX, AY: Single);
  108. begin
  109.   inherited Create(APosition);
  110.   X := AX;
  111.   Y := AY;
  112. end;
  113. { TAlignTag }
  114. constructor TAlignTag.Create(APosition: Integer; AAlign: Integer);
  115. begin
  116.   inherited Create(APosition);
  117.   Align := AAlign;
  118. end;
  119. { TLinkTag }
  120. constructor TLinkTag.Create(APosition: Integer; const AName: string);
  121. begin
  122.   inherited Create(APosition);
  123.   Name := AName;
  124. end;
  125. { TMarkup }
  126. procedure TMarkup.ParseFormatting;
  127. begin
  128.   FTotalTags := 0;
  129.   SetLength(FTags, 0);
  130. end;
  131. procedure TMarkup.AddTag(const Tag: TTag);
  132. begin
  133.   Inc(FTotalTags); SetLength(FTags, FTotalTags);
  134.   FTags[TotalTags-1] := Tag;
  135. end;
  136. procedure TMarkup.InsertTag(Pos: Integer; const Tag: TTag);
  137. var i: Integer;
  138. begin
  139.   AddTag(Tag);
  140.   for i := FTotalTags-2 downto Pos do FTags[i+1] := FTags[i];
  141.   FTags[Pos] := Tag;
  142. end;
  143. procedure TMarkup.SetFText(const Value: string);
  144. begin
  145.   if Value = FText then Exit;
  146.   FText  := Value;
  147.   Parsed := False;
  148. end;
  149. function TMarkup.GetPureText: string;
  150. begin
  151.   if not Parsed then ParseFormatting;
  152.   Result := FPureText;
  153. end;
  154. function TMarkup.GetTag(Index: Integer): TTag;
  155. begin
  156.   if not Parsed then ParseFormatting;
  157.   Result := FTags[Index];
  158. end;
  159. constructor TMarkup.Create;
  160. begin
  161.   SeparatorChars := ' +-*/<>.,'#10#13;
  162.   InvisibleChars := ' -'#10#13;
  163. end;
  164. destructor TMarkup.Destroy;
  165. var i: Integer;
  166. begin
  167.   for i := 0 to FTotalTags-1 do FreeAndNil(FTags[i]);
  168.   FTotalTags := 0;
  169.   FTags := nil;
  170.   FText := ''; FPureText := '';
  171.   inherited;
  172. end;
  173. procedure TMarkup.Invalidate;
  174. begin
  175.   Parsed := False;
  176. end;
  177. { TSimpleMarkup }
  178. procedure TSimpleMarkup.ParseFormatting;
  179. // Syntax: "["<Command>[nn]"]"
  180. const
  181.   sNone = 0; sBeginCmd = 1;
  182.   OpenBracket = '['; CloseBracket = ']';
  183.   amLeft = 0; amCenter = 1; amRight = 2; amJustify = 3;
  184. var
  185.   i, PureLength: Integer;
  186.   Argument: string;
  187.   Command: Char;
  188.   State, AlignMode: Cardinal;
  189. //
  190.   LineWidth, LineHeight, WordWidth, WordHeight, CharWidth: Single;
  191.   CurrentWord: string;
  192.   CurFont: TFont;
  193.   MaxWidth, CurY: Single;
  194.   LastSeparator: Char;
  195.   LastTagPosition: Integer;
  196.   
  197.   LastNewLineTag: TMoveToTag;
  198.   procedure ScanArgument;                     // ToDo: optimize
  199.   begin
  200.     Inc(i);
  201.     Argument := '';
  202.     while (i <= Length(FText)) and (FText[i] <> CloseBracket) do begin
  203.       Argument := Argument + FText[i];
  204.       Inc(i);
  205.     end;
  206.   end;
  207.   procedure AdjustAlign;
  208.   begin
  209.     if LastNewLineTag <> nil then case AlignMode of
  210.       amCenter: LastNewLineTag.X := (MaxWidth - (LineWidth -0* WordWidth)) * 0.5;
  211.       amRight:  LastNewLineTag.X := (MaxWidth - (LineWidth -0* WordWidth));
  212.     end;
  213.   end;
  214.   procedure NewLine;
  215.   begin
  216.     CurY       := CurY + LineHeight;
  217.     AdjustAlign;
  218.     LineWidth  := WordWidth + CharWidth;
  219.     LineHeight := WordHeight;
  220.     // Add a MoveTo tag after an invisible separator but before a visible one
  221.     LastNewLineTag := TMoveToTag.Create(MaxI(0, Length(FPureText) + 0*Ord(WordWidth = 0)), 0, CurY);
  222.     InsertTag(LastTagPosition, LastNewLineTag);
  223.   end;
  224.   procedure AddToPureText(Ch: Char);      // ToFix: coupled visible separators can exceed the area
  225.     procedure AddChar;
  226.     begin
  227.       CurrentWord := CurrentWord + Ch;
  228.       Inc(PureLength);
  229.     end;
  230.   begin
  231.     if (Pos(Ch, SeparatorChars) > 0) or (Ch = #0) then begin                  // Separator character
  232.       CurFont.GetTextExtent(Ch, CharWidth, WordHeight);
  233.       CurFont.GetTextExtent(CurrentWord, WordWidth, WordHeight);
  234.       if (LineWidth + WordWidth + CharWidth > MaxWidth) and (LineWidth > 0) then begin // A new line needed
  235.         if LineWidth + WordWidth <= MaxWidth then begin                       // But it fits without separator
  236.           FPureText := FPureText + CurrentWord;
  237.           if Pos(Ch, InvisibleChars) > 0 then begin                           // The separator is invisible
  238.             FPureText := FPureText + Ch;
  239.             WordWidth := 0;
  240.             CharWidth := 0;
  241.             Ch := #0;
  242.           end else WordWidth := CharWidth;
  243.           CurrentWord := '';
  244.         end;
  245.         NewLine;
  246.       end else LineWidth  := LineWidth + WordWidth + CharWidth;
  247.       LineHeight := MaxS(LineHeight, WordHeight);
  248.       if Ch <> #0 then begin                                                  // Add Ch's width and height to line
  249.         AddChar;
  250.         CurFont.GetTextExtent(Ch, WordWidth, WordHeight);
  251. //         LineWidth  := LineWidth + WordWidth;
  252.         LineHeight := MaxS(LineHeight, WordHeight);
  253.       end;
  254.       FPureText := FPureText + CurrentWord;
  255.       CurrentWord := '';
  256.       LastSeparator := Ch;
  257.       LastTagPosition := TotalTags;
  258.     end else if Ch <> #0 then AddChar;
  259.   end;
  260. begin                                              // ToDo: optimize
  261.   inherited;
  262.   if DefaultFont = nil then Exit;
  263.   i := 1;
  264.   CurY := 0;
  265.   CurFont := DefaultFont;
  266.   MaxWidth := DefaultWidth;
  267.   FPureText   := '';
  268.   CurrentWord := '';
  269.   LineWidth  := 0;
  270.   LineHeight := 0;
  271.   CharWidth  := 0;
  272.   WordWidth  := 0;
  273.   WordHeight := 0;
  274.   PureLength := 0;
  275.   LastSeparator := #0;
  276.   LastTagPosition := 0;
  277.   State := sNone;
  278.   AlignMode := amRight;
  279.   AlignMode := amLeft;
  280.   LastNewLineTag := nil;
  281.   NewLine;
  282.   while i <= Length(FText) do begin
  283.     case State of
  284.       sNone:     if FText[i] = OpenBracket then State := sBeginCmd else AddToPureText(FText[i]);
  285.       sBeginCmd: if FText[i] = OpenBracket then begin            // Escape symbol occured
  286.         AddToPureText(FText[i]);
  287.         State := sNone;
  288.       end else begin                                             // Process command
  289.         Command := UpCase(FText[i]);
  290.         ScanArgument;
  291.         case Command of
  292.           '#': if Argument <> '' then begin
  293.             if Length(Argument) = 6 then                         // Preserve alpha component
  294.               AddTag(TColorTag.Create(PureLength, GetColor(HexStrToIntDef(Argument, 0)))) else
  295.                 AddTag(TAlphaColorTag.Create(PureLength, GetColor(HexStrToIntDef(Argument, 0))));
  296.           end else AddTag(TColorResetTag.Create(PureLength));
  297.           'B': ;
  298.           'I': ;
  299.           '_': ;
  300.           'E': NewLine;
  301.           else {$IFDEF LOGGING} Log.Log(ClassName + '.ParseFormatting: Unknown command "' + Command + '"', lkWarning); {$ENDIF}
  302.         end;
  303.         State := sNone;
  304.       end;
  305.     end;
  306.     Inc(i);
  307.   end;
  308.   AddToPureText(#0);                        // Handle last word
  309.   AdjustAlign;                              // Check if this already called by previous line of code
  310.   Parsed := True;
  311. end;
  312. end.