Некоторые изменения

отредактировано 23:49 Раздел: FastReport 3.0
Предлагаю несколько изменений в исходные тексты FR3. Если техподдержка не сочтет нужным внести эти изменения в очередной релиз, читатели форума могут сделать это сами. Эти изменения относятся к версии 3.20 (стабильная версия), хотя, если верить change log, к более поздним версиям эти изменения тоже можно применить.

1. Преобразование отчетов из формата FR2x в формат FR3 (реализовано в модуле frx2xto30.pas)

- ОШИБКА: если отчет FR2x содержал подотчеты (subreports), то при построении такого отчета в FR3 возникала ошибка Access Violation. Это происходило из-за ошибки в функции TfrSubreportLoadFromStream. Исправленная версия:
procedure TfrSubreportLoadFromStream;
var
  s: TfrxSubreport;
  SubPage: Integer;
begin
  TfrViewLoadFromStream;
  s := TfrxSubreport.Create(Page);
  SetfrxComponent(s);
  Stream.Read(SubPage, 4);
  s.Page := TfrxReportPage(Report.Pages[SubPage]);
  if s.Page.Name = '' then
    s.Page.CreateUniqueName;

  s.Page.LeftMargin := 0;
  s.Page.RightMargin := 0;
  s.Page.TopMargin := 0;
  s.Page.BottomMargin := 0;
end;

- ОШИБКА: При преобразовании теряются значения DisplayFormat, и в FR3 это свойство приходится устанавливать заново. Вот измененная функция TfrMemoViewLoadFromStream, частично решающая эту проблему преобразование числовых форматов в некоторых случаях по-прежнему происходит некорректно).
procedure TfrMemoViewLoadFromStream;
var
  w: Word;
  i: Integer;
  Alignment: Integer;
  Highlight: TfrHighlightAttr;
  HighlightStr: String;
  LineSpacing, CharacterSpacing: Integer;
  m: TfrxMemoView;

  procedure DecodeDisplayFormat;
  var
    LCategory: Byte;
    LType: Byte;
    LNoOfDecimals: Byte;
    LSeparator: Char;
  begin
    LCategory := (Format and $0F000000) shr 24;
    LType := (Format and $00FF0000) shr 16;
    LNoOfDecimals := (Format and $0000FF00) shr 8;
    LSeparator := Chr(Format and $000000FF);

    case LCategory of
      0: { text }
        m.DisplayFormat.Kind := fkText;

      1: { number }
      begin
        m.DisplayFormat.Kind := fkNumeric;
        m.DisplayFormat.DecimalSeparator := LSeparator;
        case LType of
          0: m.DisplayFormat.FormatStr := '%2.' + IntToStr(LNoOfDecimals) + 'g';
          1: m.DisplayFormat.FormatStr := '%g';
          2: m.DisplayFormat.FormatStr := '%2.' + IntToStr(LNoOfDecimals) + 'f';
          3: m.DisplayFormat.FormatStr := '%2.' + IntToStr(LNoOfDecimals) + 'n';
          else
           m.DisplayFormat.FormatStr := '%g' { can't convert custom format string };
        end;
      end;

      2: { date }
      begin
        m.DisplayFormat.Kind := fkDateTime;
        case LType of
          0: m.DisplayFormat.FormatStr := 'dd.mm.yy';
          1: m.DisplayFormat.FormatStr := 'dd.mm.yyyy';
          2: m.DisplayFormat.FormatStr := 'd mmm yyyy';
          3: m.DisplayFormat.FormatStr := LongDateFormat;
          4: m.DisplayFormat.FormatStr := FormatStr;
        end;
      end;

      3: { time }
      begin
        m.DisplayFormat.Kind := fkDateTime;
        case LType of
          0: m.DisplayFormat.FormatStr := 'hh:nn:ss';
          1: m.DisplayFormat.FormatStr := 'h:nn:ss';
          2: m.DisplayFormat.FormatStr := 'hh:nn';
          3: m.DisplayFormat.FormatStr := 'h:nn';
          4: m.DisplayFormat.FormatStr := FormatStr;
        end;
      end;

      4: { boolean }
      begin
        m.DisplayFormat.Kind := fkBoolean;
        case LType of
          0: m.DisplayFormat.FormatStr := '0,1';
          1: m.DisplayFormat.FormatStr := 'Нет,Да';
          2: m.DisplayFormat.FormatStr := '_,X';
          3: m.DisplayFormat.FormatStr := 'False,True';
          4: m.DisplayFormat.FormatStr := FormatStr;
        end;
      end;
    end;
  end;

begin
  TfrViewLoadFromStream;
  m := TfrxMemoView.Create(Page);
  SetfrxComponent(m);
  SetfrxView(m);

  with Stream do
  begin
    { font info }
    m.Font.Name := ReadString(Stream);
    Read(i, 4);
    m.Font.Size := i;
    Read(w, 2);
    m.Font.Style := frSetFontStyle(w);
    Read(i, 4);
    m.Font.Color := i;

    { text align, rotation }
    Read(Alignment, 4);
    if (Alignment and frtaRight) <> 0 then
      m.HAlign := haRight;
    if (Alignment and frtaCenter) <> 0 then
      m.HAlign := haCenter;
    if (Alignment and 3) = 3 then
      m.HAlign := haBlock;
    if (Alignment and frtaVertical) <> 0 then
      m.Rotation := 90;
    if (Alignment and frtaMiddle) <> 0 then
      m.VAlign := vaCenter;
    if (Alignment and frtaDown) <> 0 then
      m.VAlign := vaBottom;

    { charset }
    Read(w, 2);
    if frVersion < 23 then
      w := DEFAULT_CHARSET;
    m.Font.Charset := w;

    Read(Highlight, 10);
    HighlightStr := ReadString(Stream);

    m.Highlight.Condition := HighlightStr;
    m.Highlight.Color := Highlight.FillColor;
    m.Highlight.Font.Color := Highlight.FontColor;
    m.Highlight.Font.Style := frSetFontStyle(Highlight.FontStyle);

    if frVersion >= 24 then
    begin
      Read(LineSpacing, 4);
      m.LineSpacing := LineSpacing;
      Read(CharacterSpacing, 4);
      m.CharSpacing := CharacterSpacing;
    end;
  end;

  if frVersion = 21 then
    Flags := Flags or flWordWrap;

  if (Flags and flStretched) <> 0 then
    m.StretchMode := smMaxHeight;
  m.WordWrap := (Flags and flWordWrap) <> 0;
  m.WordBreak := (Flags and flWordBreak) <> 0;
  m.AutoWidth := (Flags and flAutoSize) <> 0;
  m.AllowExpressions := (Flags and flTextOnly) = 0;
  m.SuppressRepeated := (Flags and flSuppressRepeated) <> 0;
  m.HideZeros := (Flags and flHideZeros) <> 0;
  m.Underlines := (Flags and flUnderlines) <> 0;
  m.RTLReading := (Flags and flRTLReading) <> 0;

  DecodeDisplayFormat;

  ConvertMemoExpressions(m, Memo.Text);
end;

- ОШИБКА: Если в программу добавить модуль frx2xto30.pas, то изменяется поведение Pascal-скрипта: становится возможным не описывать переменные. Причем это изменение - глобальное, а не только для сконвертированных отчетов. В документации об этом упоминания нет. Более того, в таком режиме Pascal-скрипт работает не так, как работал скрипт FR2x: в FR2x переменная при первом обращении неявно описывалась как глобальная; а в FR3 (с включенным frx2xto30.pas) не описанная переменная неявно описывается как локальная для данной процедуры, при этом наличие глобальных переменных не проверяется. В результате становится невозможным использовать в скрипте глобальные (т.е. описанные вне всех процедур) переменные. Очевидно, налицо ошибка в логике обработки параметра <declarevars text="0"/> в XML-описании синтаксиса скрипта. В данном случае более предсказуемого поведения удалось добиться, закомментировав вызов процедуры fsModifyPascalForFR2 в секции initialization модуля frx2xto30.pas.

2. Как выяснилось, FastScript предоставляет крайне примитивные возможности по диагностике ошибок времени выполнения. Во многих случаях про возникновении ошибки во время выполнения скрипта не выдается никаких подсказок о месте возникновения ошибки. Это сильно затрудняет отладку, особенно на больших скриптах. Поэтому, для улучшения ситуации, в функции TfsScript.CallFunction и TfsScript.CallFunction1 (в модуле fs_iinterpreter.pas) добавлена обработка исключений так, чтобы в сообщении присутствовало имя функции скрипта, при выполнении которой произошла ошибка.

Вот текст измененных функций:
function TfsScript.CallFunction(const Name: String; const Params: Variant): Variant;
var
  i: Integer;
  v: TfsCustomVariable;
  p: TfsProcVariable;
begin
  v := FindLocal(Name);
  if (v <> nil) and (v is TfsProcVariable) then
  begin
    p := TfsProcVariable(v);

    if VarIsArray(Params) then
      for i := 0 to VarArrayHighBound(Params, 1) do
        p.Params[i].Value := Params[i];

    try
      Result := p.Value;
    except
      on e: Exception do
      begin
        e.Message := 'Ошибка при исполнении функции "' + v.Name + '": ' + e.Message;
        raise;
      end;
    end;

  end
  else
  begin
    Result := Null;
  end;
end;

function TfsScript.CallFunction1(const Name: String; var Params: Variant): Variant;
var
  i: Integer;
  v: TfsCustomVariable;
  p: TfsProcVariable;
begin
  v := FindLocal(Name);
  if (v <> nil) and (v is TfsProcVariable) then
  begin
    p := TfsProcVariable(v);

    if VarIsArray(Params) then
      for i := 0 to VarArrayHighBound(Params, 1) do
        p.Params[i].Value := Params[i];

    try
      Result := p.Value;
    except
      on e: Exception do
      begin
        e.Message := 'Ошибка при исполнении функции "' + v.Name + '": ' + e.Message;
        raise;
      end;
    end;

    if VarIsArray(Params) then
      for i := 0 to VarArrayHighBound(Params, 1) do
        Params[i] := p.Params[i].Value;
  end
  else
    Result := Null;
end;

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.