퀵레포트 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;
그럼 모두들 즐거운 시간 되시길 바라며.. ^^