Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[55] 퀵레포트 4.03 Text Search 기능 버그 수정
박진수 [jeensoo] 5490 읽음    2004-09-21 17:16
퀵레포트 4.03 Text Search 기능 버그 수정



많은 분들이 델파이를 사용하면서 접했던 레포트가 퀵레포트(이하 퀵) 일 것입니다.
손에 익숙해서 저를 비롯하여 많은 분들이 쉽게 바꾸지 못하게 되더군요.

퀵 4.03에서는 새롭게 추가된 기능중 가장 두들어진 기능이 Thumnail 기능과 Text Search 기능입니다.

그런데 퀵 4.03 을 테스트 하는 도중 한글 Search가 되지 않는 버그가 있더군요.

그리고 또 하나, 찾은후에 찾은 결과를 ListBox에 나열을 해줄뿐 ListBox를 클릭하면 찾은 페이지를 클릭하면
해당 페이지로 점프를 해야 하나 그런 기능도 없구요.

그래서 한글 Search가 되지 않는 버그와 찾은 결과 페이지를 클릭하면 해당 페이지로 바로 이동하는 기능을
추가했습니다.

일단 한글 textsearch 기능은 "qrprev.pas"에서
QuickRep1.Preview시에 나타나는 기본 미리보기 화면에서
망원경 그림버튼을 누르면 FindButtonClick 클릭 이벤트가 실행됩니다.

procedure TQRStandardPreview.FindButtonClick(Sender: TObject);
var
  FindStr : string;
  MatchCase : boolean;
  I : integer;
  APage : TMetafile;
  Dummy : TList;
begin
  if GetTextSearch(MatchCase, FindStr) then  <---  찾을단어를 입력받는 다이얼로그창 호출
  begin
    SearchResultBox.Enabled := true;
    SearchTextLabel.Caption := FindStr;
    SearchResultBox.Items.Clear;
    for I := 1 to QRPrinter.PageCount do  
    begin
       APage := QRPrinter.GetPage(I);  <---- 레포트 페이지별 메타파일 추출
       try
         if StrInMetafile(FindStr, APage, MatchCase) then  <-- 메타파일내에서 TextSearch를하기위해 내부함수호출
            SearchResultBox.Items.Add('Page ' + IntToStr(I));
       finally
         APage.Free;
       end;
    end;
    if SearchResultBox.Items.Count = 0 then
    begin
      SearchResultBox.Items.Add('찾고자하는 단어가 없습니다.');
      SearchResultBox.Enabled := false;
    end;
  end;
end;


QrPrev.pas에서 사용되는 내부호출 함수는 StrInMetafile로 QrExtra.pas와 QrPrntr.pas에 있던데..
원 소스에서는 이름과 처리 로직은 같은데..  퀵레포트를 만든곳에서 소스정리를 잘못했던지..
서로 약간 틀리더군요..

QrPrntr.pas에서는  다음과 같고
type
  bar = array of char;   <<<<********  이곳
var
  MFSearchStr : string;
  MFFound : boolean;
  MFMatchCase : boolean;

function MetaEnum(DC : THandle; HandleTable : pointer; MetaRec : Pointer; Count : word; dummy : pointer) : shortint stdcall;
var
  aStr : string;
  Ofs : integer;
  Len : integer;
  I : integer;
begin
  with tagENHMETARECORD(MetaRec^) do
  begin
    case iType of
      EMR_EXTTEXTOUTW,
      EMR_EXTTEXTOUTA : begin
                          aStr := '';
                          Ofs := (tagEMREXTTEXTOUTA(Metarec^).EMRText.offString);
                          Len := (tagEMREXTTEXTOUTA(Metarec^).EMRText.nChars);
                          SetLength(aStr, Len);
                          for I := 0 to len - 1 do
                            aStr[I+1] := char(bar(MetaRec^)[ofs + (I*2)]);  <<***** 이곳
                        end;
    end;
  end;
  if not MFMatchCase then aStr := AnsiUppercase(AStr);
  if Pos(MFSearchStr, aStr) > 0 then
  begin
    Result := 0;
    MFFound := true;
  end else
    Result := 1;
end;

function StrInMetafile(AString : string; AMetafile : TMetafile; MatchCase : boolean) : boolean;
begin
  if MatchCase then
    MFSearchStr := AString
  else
    MFSearchStr := AnsiUppercase(AString);
  MFFound := false;
  MFMatchCase := MatchCase;
  EnumEnhMetafile(0, AMetafile.handle, @MetaEnum, nil, rect(0,0,0,0));
  Result := MFFound;
end;


QrExtra.pas에는 다음과 같습니다.
type
  MFBar = array of char;  // <<<**********  이곳
var
  MFSearchStr : string;
  MFFound : boolean;
  MFMatchCase : boolean;
  MFSearchBusy : boolean;

function MetaEnum(DC : THandle; HandleTable : pointer; MetaRec : Pointer; Count : word; dummy : pointer) : shortint stdcall;
var
  aStr : string;
  Ofs : integer;
  Len : integer;
  I : integer;
begin
  aStr := '';
  with tagENHMETARECORD(MetaRec^) do
  begin
    case iType of
      EMR_EXTTEXTOUTW,
      EMR_EXTTEXTOUTA : begin
                          aStr := '';
                          Ofs := (tagEMREXTTEXTOUTA(Metarec^).EMRText.offString);
                          Len := (tagEMREXTTEXTOUTA(Metarec^).EMRText.nChars);
                          SetLength(aStr, Len);
                          for I := 0 to len - 1 do
                            aStr[I+1] := MFBar(MetaRec)[ofs + (I*2)];   <<********  이곳
                        end;
    end;
  end;
  if not MFMatchCase then aStr := AnsiUppercase(AStr);
  if Pos(MFSearchStr, aStr) > 0 then
  begin
    Result := 0;
    MFFound := true;
  end else
    Result := 1;
end;

function StrInMetafile(AString : string; AMetafile : TMetafile; MatchCase : boolean) : boolean;
begin
  while MFSearchBusy do Application.ProcessMessages;
  MFSearchBusy := true;
  if MatchCase then
    MFSearchStr := AString
  else
    MFSearchStr := AnsiUppercase(AString);
  MFFound := false;
  MFMatchCase := MatchCase;
  EnumEnhMetafile(0, AMetafile.handle, @MetaEnum, nil, rect(0,0,0,0));
  Result := MFFound;
  MFSearchBusy := false;
end;

***** 표시를 해놓은곳이 서로 틀린곳입니다.

QrPrev.pas 호출된 StrInMetafile 함수는 디버깅 결과 QrExtra.pas의 것을 호출하고 있더군요.
QrPrev.pas에서 text search는 내부함수인 StrInMetafile 함수를 호출하여, 추출한 레포트 페이지의
메타파일을 넘겨주고, StrInMetafile 함수는 win32함수인 EnumEnhMetafile 과 EnumEnhMetafile의 콜백
함수를 이용하여 메타파일내의 메타레코드를 추출하여 text search를 구현 합니다.

그런데 아쉽게도 퀵레포트사에서 작성한 원소스는 한글은 전혀 찾지 못하는 심각한 버그가 있었습니다.

그래서 실제적으로 메타레코드내에서 문자열을 추출하는 역할하는 콜백함수 내용을 다음과 같이 수정했습니다.
QrExtra.pas 및 QrPrntr.pas 모두 같이 적용하면 됩니다.
단 QrExtra.pas 및 Qrprntr.pas에서 선언한 타입인
type
  MFBar = array of char;  과
type
  bar = array of char;   부분은 삭제 하십시오.

아래는 메타레코드를 추출하는 수정된 콜백함수 입니다.
function MetaEnum(DC : THandle; HandleTable : pointer; MetaRec : Pointer; Count : word; dummy : pointer) : shortint stdcall;
var
  aStr : string;
  Ofs : integer;
  Len : integer;
begin
  aStr := '';
  if(tagENHMETARECORD(MetaRec^).iType in [EMR_EXTTEXTOUTW, EMR_EXTTEXTOUTA]) then
  begin
    aStr := '';
    Ofs := (tagEMREXTTEXTOUTA(Metarec^).EMRText.offString);
    Len := (tagEMREXTTEXTOUTA(Metarec^).EMRText.nChars);
    aStr := WideCharLenToString(PWideChar(PChar(Metarec) + Ofs), Len);
  end;
  if not MFMatchCase then aStr := AnsiUppercase(AStr);
  if Pos(MFSearchStr, aStr) > 0 then
  begin
    Result := 0;
    MFFound := true;
  end else
    Result := 1;
end;


원랙 퀵레포트사의 소스보다 훨씬 간결 합니다.
메타레코드내에서 한글은 유니코드로 표현될것 같아.. 유니코드를 Single 또는 MultiByte 스트링으로 변환하는
함수인 WideCharLenToString를 이용하여 문자열을 추출하니 되더군요.

그리고

원래 소스에서는  검색한 결과를 listbox에 'Page 1' 형식으로 표현해 주는 데 정작 list box의 해당
페이지 item을 클릭하면 페이지 이동이 되질 않아 이것이 되게끔 listbox의 Click 이벤트를 프로그램
해준 부분 입니다.
procedure TQRStandardPreview.SearchResultBoxClick(Sender: TObject);
var
   str1: string;
begin
     // 기존 4.03 기능에 새로이 추가한 기능
     str1 := '';
     if(SearchResultBox.Enabled) then // 검색된 결과가 있을시.. 해당페이지로 바로 이동
     begin
          str1 := SearchResultBox.Items.Strings[SearchResultBox.ItemIndex];
          if(Pos('Page ', str1) > 0) then
          begin
               SelectPage(StrToInt('0'+Copy(str1, 6, Length(str1)-5)));
          end;
     end;
end;


그럼 모두들 즐거운 시간 되시길 바라며.. ^^

+ -

관련 글 리스트
55 퀵레포트 4.03 Text Search 기능 버그 수정 박진수 5490 2004/09/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.