{*******************************************************} { } { ProcessWatermark } { } { Copyright (C) 2025 kku } { } {*******************************************************} unit ProcessWM_GEC; interface uses Winapi.Windows, System.Classes, Vcl.Graphics; function ProcessWartermark_GEC(DC: HDC; bStartPage: Boolean = true): Boolean; implementation uses AppCtrlDefine, ApiHookPrint, BS1Hook, Tocsg.Safe, System.SysUtils, Condition, Tocsg.Graphic, Winapi.GDIPAPI, Tocsg.Strings, Vcl.Imaging.jpeg; function ProcessWartermark_GEC(DC: HDC; bStartPage: Boolean = true): Boolean; var nW, nH, i, nX, nY, nWW, nHH, nXX, nYY, nGapW, nGapH: Integer; sText, sOut, sImgPath: String; MemCanvas: TCanvas; hbmp, hOldBmp: HBITMAP; pen: HPEN; CtrlOpt: TAppCtrlOpt; oldColor: TColor; hOldFont: HFONT; nDPI, nFontH, nFontHmax: Integer; // 만들어 놓은 BMP를 재사용하면 배경 투명처리가 안되서 이렇게 사용 23_1221 08:15:33 kku function CreateTextBmp(sOutText: String): TBitmap; begin Result := TBitmap.Create; Result.PixelFormat := pf4bit; Result.Canvas.Font.Assign(MemCanvas.Font); // Result.Canvas.Font.Color := clSilver; // if nFontSize <> -1 then // Result.Canvas.Font.Size := nFontSize; Result.SetSize(Result.Canvas.TextWidth(sOutText), Result.Canvas.TextHeight(sOutText)); // Result.Canvas.Brush.Color := clWhite; // Result.Canvas.Brush.Style := bsSolid; // SetBkMode(Result.Canvas.Handle, TRANSPARENT); SetBkColor(Result.Canvas.Handle, RGB(255, 255, 255)); Result.Canvas.TextOut(0, 0, sOutText); Result.TransparentColor := clWhite; Result.Transparent := true; end; begin Result := true; // if _nRecentPage < gAppHook.Helper.nPtrCnt_ then // _nRecentPage := gAppHook.Helper.nPtrCnt_ // else exit; // if _nRecentPage > 0 then // exit; // _nRecentPage := 1; // 엑셀 (excel.exe)의 경우 이렇게 안하면 다음 페이지로 밀리는 현상 있음. // 아마도 DC를 별도 추가 처리 하는 듯 함 23_1024 11:00:13 kku if _PrtDC <> DC then exit; if _bIgrPrtWater then exit; // if gAppHook.Helper.IsExcel then // begin // if bStartPage then // LogToReg(Format('WATER_Excel_ST_%d', [_nTest]), Format('DC=%d, W=%d, H=%d', [DC, GetDeviceCaps(DC, HORZRES), GetDeviceCaps(DC, VERTRES)])) // else // LogToReg(Format('WATER_Excel_ED_%d', [_nTest]), Format('DC=%d, W=%d, H=%d', [DC, GetDeviceCaps(DC, HORZRES), GetDeviceCaps(DC, VERTRES)])); // Inc(_nTest); // end; bStartPage := false; SaveDC(DC); gAppHook.Helper.bIsWaterMaking_ := true; try try // nW := GetDeviceCaps(DC, PHYSICALWIDTH); // 세로모드 : 4961, 가로모드 : 7016, 크로미움 프린트 인쇄시 4760 // nH := GetDeviceCaps(DC, PHYSICALHEIGHT); // 가로모드 : 7016, 가로모드 : 4961, 크로미움 프린트 인쇄시 6814 nW := GetDeviceCaps(DC, HORZRES); // 세로모드 : 4961, 가로모드 : 7016, 크로미움 프린트 인쇄시 4760 nH := GetDeviceCaps(DC, VERTRES); // 가로모드 : 7016, 가로모드 : 4961, 크로미움 프린트 인쇄시 6814 nX := GetDeviceCaps(DC, ASPECTX); nY := GetDeviceCaps(DC, ASPECTY); // 인도현지 프린터 종류 // 캐논 IRC3326, 쿄세라(kyocera) M8124 nDPI := GetDeviceCaps(DC, LOGPIXELSX); // LogToReg('PrinterDPI', IntToStr(nDPI)); if nDPI < 600 then begin // 인도 현지 캐논 프린트 최대 DPI가 300이다 24_0621 10:22:10 kku nFontH := 70; nFontHmax := 80; end else begin nFontH := 180; nFontHmax := 200; end; // 작업 후 폰트 롤백을 위한 백업 23_1018 11:21:29 kku hOldFont := GetCurrentObject(DC, OBJ_FONT); Guard(MemCanvas, TCanvas.Create); if bStartPage then // 필요없음 MemCanvas.Handle := CreateCompatibleDC(DC) else MemCanvas.Handle := DC; //CreateCompatibleDC(DC); hbmp := CreateCompatibleBitmap(MemCanvas.Handle, nW, nH); hOldBmp := SelectObject(MemCanvas.Handle, hbmp); gAppHook.Log(Format('ProcessWartermark_GEC() - DocSize X=%d, Y=%d, W=%d, H=%d, dc=%d', [nX, nY, nW, nH, DC])); if bStartPage then PatBlt(MemCanvas.Handle, 0, 0, nW, nH, WHITENESS); nGapW := 0; nGapH := 0; // SetBkMode(MemCanvas.Handle, TRANSPARENT); oldColor := GetTextColor(MemCanvas.Handle); // 폰트색 지정... 프로그램에 따라 지정되는 설정이 다르다 MemCanvas.Font.Color := clGray; // 메모장등 SetTextColor(MemCanvas.Handle, clGray); // 엑셀등 // MemCanvas.Font.Name := 'Tahoma'; // MemCanvas.Font.Name := '돋음'; if gAppHook.Helper.bEndDocProc_ then begin if (CompareText(gAppHook.ModuleName, 'winword.exe') = 0) or (CompareText(gAppHook.ModuleName, 'MSIP.Viewer.exe') = 0) then nGapH := 85; end; MemCanvas.Font.Size := 60; // sOut := gAppHook.Helper.CtrlOpt.sPrintWaterTxt; sOut := ''; if gAppHook.Helper.CtrlOpt.sDeptName <> '' then SumString(sOut, gAppHook.Helper.CtrlOpt.sDeptName, '/'); if gAppHook.Helper.CtrlOpt.sEmpNo <> '' then SumString(sOut, gAppHook.Helper.CtrlOpt.sEmpNo, '/'); if gAppHook.Helper.sPrintWaterTxt <> '' then SumString(sOut, gAppHook.Helper.sPrintWaterTxt, '/'); SumString(sOut, FormatDateTime('yyyy-mm-dd', Now), '/'); if _sbmpWaterIf <> sOut then begin // 이미지 캐시 초기화 추가 24_1213 14:05:40 kku _sbmpWaterIf := sOut; if _bmpWater <> nil then FreeAndNil(_bmpWater); if _bmpWaterP <> nil then FreeAndNil(_bmpWater); end; if _nFontSize = 0 then begin _nFontSize := MemCanvas.Font.Size; while True do begin if _nFontSize > nFontHmax then break; // if MemCanvas.TextWidth(sOut) > 3000 then // 부서명이 들어간 후 2000에서 변경 if MemCanvas.TextHeight(sOut) > nFontH then // 부서명이 들어간 후 2000에서 변경 begin Dec(_nFontSize); break; end; Inc(_nFontSize); MemCanvas.Font.Size := _nFontSize; end; end; if nDPI < 600 then MemCanvas.Font.Size := _nFontSize - Round(_nFontSize / 2.5) else MemCanvas.Font.Size := _nFontSize - (_nFontSize div 3); // sOut := IntToStr(MemCanvas.Font.Size) + ' - ' + sOut; try if gAppHook.Helper.bSmallFont_ then begin // 브라우저등의 경우... 폰트 크기를 가늠할수가 없다. // 그래서 아래처럼 이미지로 처리 23_1017 13:10:40 kku // var bmpText: TBitmap; // Guard(bmpText, TBitmap.Create); // bmpText.PixelFormat := pf4bit; // bmpText.Canvas.Font.Assign(MemCanvas.Font); // bmpText.Canvas.Font.Size := 12; // bmpText.Canvas.Brush.Color := clWhite; //// bmpText.Canvas.Brush.Style := bsClear; //// bmpText.font // sText := '작은 습관으로 시작되는 보안'; // bmpText.SetSize(bmpText.Canvas.TextWidth(sText), bmpText.Canvas.TextHeight(sText)); // bmpText.Canvas.TextOut(0, 0, sText); // MemCanvas.Draw(nW - bmpText.Width + nGapW + nGapW2, nH - bmpText.Height + nGapH + nGapH2, bmpText); // 음..브라우저에서 상하단 문구 제거 23_1017 13:26:47 kku end else begin // var sDocInfo: String := 'c:\ProgramData\HE\Task\$TEST.txt'; // begin // if FileExists(sDocInfo) then // begin // var StrList: TStringList; // Guard(StrList, TStringList.Create); // StrList.LoadFromFile(sDocInfo, TEncoding.UTF8); // DeleteFile(sDocInfo); // if StrList.Text <> '' then // begin // sText := StrList.Text; // MemCanvas.TextOut(nGapW - MemCanvas.TextWidth(sText), nGapH, Format('%s', [sText])); // end; // end; // end; var bmpText: TBitmap; var rcText: TRect; // 우측 상단 if _sLabelName <> '' then begin sText := _sLabelName; if nDPI > 300 then sText := sText + ' '; // 쿄세라 프린터에서 오른쪽 짤리는 현상이 있어서 공백 하나 넣어줌 24_0621 11:11:28 kku Guard(bmpText, CreateTextBmp(sText)); ZeroMemory(@rcText, SizeOf(rcText)); rcText.Left := nW - bmpText.Width; rcText.Right := rcText.Left + bmpText.Width + 1; rcText.Top := 0; rcText.Bottom := bmpText.Height + 1; // MemCanvas.Draw(nW - bmpText.Width, 0, bmpText); MemCanvas.StretchDraw(rcText, bmpText); // 이렇게 해야 프린터에서 인식함.. 24_0108 14:25:59 kku // MemCanvas.TextOut(nW - MemCanvas.TextWidth(_sLabelName) + nGapW, nGapH, _sLabelName); end; // 우측 하단 begin sText := 'Security Starts with Small Habits'; if nDPI > 300 then sText := sText + ' '; // 쿄세라 프린터에서 오른쪽 짤리는 현상이 있어서 공백 하나 넣어줌 24_0621 11:11:28 kku Guard(bmpText, CreateTextBmp(sText)); ZeroMemory(@rcText, SizeOf(rcText)); rcText.Left := nW - bmpText.Width + nGapW; rcText.Right := rcText.Left + bmpText.Width + 1; rcText.Top := nH - bmpText.Height; rcText.Bottom := rcText.Top + bmpText.Height + 1; // 왜인지는 모르겠지만.. 아래 텍스트와는 다르게 세로 갭을 넣을 필요가 없다... (winword.exe) 23_1211 16:10:21 kku // MemCanvas.Draw(nW - bmpText.Width + nGapW, nH - bmpText.Height, bmpText); MemCanvas.StretchDraw(rcText, bmpText); // sText := Format('px=%d, 작은 습관으로 시작되는 보안', [MemCanvas.Font.PixelsPerInch]); // sText := '작은 습관으로 시작되는 보안'; // MemCanvas.TextOut(nW - MemCanvas.TextWidth(sText) + nGapW, // nH - MemCanvas.TextHeight(sText) + nGapH, sText); end; // if nDPI < 600 then MemCanvas.Font.Size := _nFontSize div 5 else MemCanvas.Font.Size := _nFontSize div 4; // 좌측 하단 // begin // sText := '본 문서는 현대엔지니어링의 정보자산이며 본 문서에 대한 무단복제 및 무단도용은 당사 사규와 관련 법규에 의해 제재를 받을 수 있습니다.'; // Guard(bmpText, CreateTextBmp(sText)); // // ZeroMemory(@rcText, SizeOf(rcText)); // rcText.Left := nGapW; // rcText.Right := rcText.Left + bmpText.Width + 1; // rcText.Top := nH - (bmpText.Height * 2); // rcText.Bottom := rcText.Top + bmpText.Height + 1; // //// MemCanvas.Draw(nGapW, nH - (bmpText.Height * 2), bmpText); // MemCanvas.StretchDraw(rcText, bmpText); // end; begin sText := 'This document or drawing is the property of Hyundai Engineering. Any reproduction or distribution of the materials without permission of Hyundai engineering is strictly prohibited.'; if nDPI > 300 then sText := ' ' + sText; // 쿄세라 프린터에서 왼쪽 짤리는 현상이 있어서 공백 넣어줌 24_0621 11:11:28 kku Guard(bmpText, CreateTextBmp(sText)); ZeroMemory(@rcText, SizeOf(rcText)); rcText.Left := nGapW; rcText.Right := rcText.Left + bmpText.Width + 1; rcText.Top := nH - bmpText.Height - 5; rcText.Bottom := rcText.Top + bmpText.Height + 1; // MemCanvas.Draw(nGapW, nH - bmpText.Height - 5, bmpText); MemCanvas.StretchDraw(rcText, bmpText); end; sText := ''; if gAppHook.Helper.CtrlOpt.sDeptName <> '' then SumString(sText, gAppHook.Helper.CtrlOpt.sDeptName, '/'); if gAppHook.Helper.CtrlOpt.sUName <> '' then SumString(sText, gAppHook.Helper.CtrlOpt.sUName, '/'); SumString(sText, FormatDateTime('yyyy-mm-dd hh:nn:ss', Now), '/'); // 좌측 상단 // begin // Guard(bmpText, CreateTextBmp(sText)); // // ZeroMemory(@rcText, SizeOf(rcText)); // rcText.Left := 0; // rcText.Right := rcText.Left + bmpText.Width + 1; // rcText.Top := 0; // rcText.Bottom := rcText.Top + bmpText.Height + 1; // //// MemCanvas.Draw(0, 0, bmpText); // MemCanvas.StretchDraw(rcText, bmpText); // end; // if nGapH = 85 then // begin // sText := '본 문서는 현대엔지니어링의 정보자산이며 본 문서에 대한 무단복제 및 무단도용은 당사 사규와 관련 법규에 의해 제재를 받을 수 있습니다.'; // MemCanvas.TextOut(nGapW, nH - (MemCanvas.TextHeight(sText) * 2) + nGapH - 65, sText); // sText := 'This document or drawing is the property of Hyundai Engineering. Any reproduction or distribution of the materials without permission of Hyundai engineering is strictly prohibited.'; // MemCanvas.TextOut(nGapW, nH - (MemCanvas.TextHeight(sText)) + nGapH - 65, sText); // end else begin // sText := '본 문서는 현대엔지니어링의 정보자산이며 본 문서에 대한 무단복제 및 무단도용은 당사 사규와 관련 법규에 의해 제재를 받을 수 있습니다.'; // MemCanvas.TextOut(nGapW, nH - (MemCanvas.TextHeight(sText) * 2) + nGapH, sText); // sText := 'This document or drawing is the property of Hyundai Engineering. Any reproduction or distribution of the materials without permission of Hyundai engineering is strictly prohibited.'; // MemCanvas.TextOut(nGapW, nH - (MemCanvas.TextHeight(sText)) + nGapH, sText); // end; end; // 워터마크 이미지 처리 23_1011 10:41:31 kku sImgPath := ExtractFilePath(gAppHook.Helper.DllPath) + 'HWMJ.dat'; if FileExists(sImgPath) then begin if _bmpWaterP = nil then begin var jpg: TJPEGImage; // Guard(jpg, TJPEGImage.Create); jpg := TJPEGImage.Create; jpg.LoadFromFile(sImgPath); var bmpImg: TBitmap; // Guard(bmpImg, TBitmap.Create); bmpImg := TBitmap.Create; bmpImg.Assign(jpg); jpg.Free; // Free // bmpImg.Canvas.Font.Name := 'Tahoma'; // bmpImg.Canvas.Font.Name := '돋음'; bmpImg.Canvas.Font.Assign(MemCanvas.Font); bmpImg.Canvas.Font.Color := clSilver; // bmpImg.Canvas.Font.Size := 40; bmpImg.Canvas.Font.Size := _nFontSize - (_nFontSize div 2); bmpImg.Canvas.Font.Style := bmpImg.Canvas.Font.Style + [fsBold]; //--- // sOut := '정보보호부문/2323308/10.177.15.123/2023-12-31'; // HEC 새로운 요구사항 반영 23_1121 10:22:09 kku // bmpImg.Canvas.Font.Size := bmpImg.Canvas.Font.Size + 14; // ScalePercentBmp(bmpImg, 120); //--- if nDPI < 600 then bmpImg.Canvas.Font.Size := bmpImg.Canvas.Font.Size + 20; nWW := bmpImg.Canvas.TextWidth(sOut); if bmpImg.Width > nWW then nWW := bmpImg.Width; nHH := bmpImg.Height + bmpImg.Canvas.TextHeight(sOut); _bmpWaterP := TBitmap.Create; _bmpWaterP.Canvas.Font.Assign(bmpImg.Canvas.Font); _bmpWaterP.PixelFormat := pf4bit; // pf32bit; _bmpWaterP.SetSize(nWW, nHH); _bmpWaterP.Canvas.Brush.Color := clWhite; _bmpWaterP.Canvas.Brush.Style := bsSolid; _bmpWaterP.Canvas.FillRect(Rect(0, 0, _bmpWaterP.Width, _bmpWaterP.Height)); // _bmpWaterP.Canvas.Brush.Style := bsClear; // x _bmpWaterP.Canvas.Draw((nWW div 2) - (bmpImg.Width div 2), 0, bmpImg); _bmpWaterP.Canvas.TextOut(0, bmpImg.Height, sOut); // _bmpWaterP.Canvas.TextOut(0, (_bmpWaterP.Height div 2) - (_bmpWaterP.Canvas.TextHeight(sOut) div 2), sOut); bmpImg.Free; // Free // RotateBitmap_STF(_bmpWaterP, -0.7, true, clWhite, 200); RotateBitmap_PlgBlt(_bmpWaterP, -0.7, true, clWhite); var ii: Integer; var jj: Integer; if gAppHook.Helper.IsExcel then begin // 도트 투명도 설정 for ii := 0 to _bmpWaterP.Width - 1 do for jj := 0 to _bmpWaterP.Height - 1 do if _bmpWaterP.Canvas.Pixels[ii, jj] <> clWhite then if ((ii + jj) mod 4) = 0 then _bmpWaterP.Canvas.Pixels[ii, jj] := clWhite else _bmpWaterP.Canvas.Pixels[ii, jj] := clSilver; end else begin for ii := 0 to _bmpWaterP.Width - 1 do for jj := 0 to _bmpWaterP.Height - 1 do if _bmpWaterP.Canvas.Pixels[ii, jj] <> clWhite then _bmpWaterP.Canvas.Pixels[ii, jj] := clGray; end; // if nDPI < 600 then // ScalePercentBmp(_bmpWaterP, 65) // else ScalePercentBmp(_bmpWaterP, 60); _bmpWaterP.TransparentColor := clWhite; _bmpWaterP.Transparent := true; end; if (_bmpWater = nil) and (_bmpWaterP <> nil) then begin // HEC 보안프린터에서 컬러 출력시 아래 부분이 검은색으로 찍히는 문제가 있다... // 이렇게 크기를 반으로 줄이고 덮어쓸때 늘리면 해결됨... 23_1103 13:42:54 kku var nIW: Integer := nW - (nW div 2); var nIH: Integer := nH - (nH div 2); if nDPI < 600 then begin nIW := nIW * 2; nIH := nIH * 2; end; _bmpWater := TBitmap.Create; _bmpWater.PixelFormat := pf4bit; // _bmpWater.SetSize(nW-1, nH-1); // 크기를 똑같이 하면 StretchDraw() 이게 무시되서 1씩 줄임 _bmpWater.SetSize(nIW, nIH); // 크기를 똑같이 하면 StretchDraw() 이게 무시되서 1씩 줄임 _bmpWater.Canvas.Brush.Color := clWhite;// clRed; _bmpWater.Canvas.Brush.Style := bsSolid; _bmpWater.Canvas.FillRect(Rect(0, 0, _bmpWater.Width, _bmpWater.Height)); // _bmpWater.Canvas.Brush.Style := bsClear; // bmpWater.SaveToFile('C:\Users\kkuzil\Desktop\출력테스트\1.bmp'); nX := (nIW div 2) - (_bmpWaterP.Width div 2); nY := (nIH div 2) - (nIH div 3); _bmpWater.Canvas.Draw(nX, nY, _bmpWaterP); // c.Draw(nX, nY, bmpWater); // BitBlt(DC, nX, nY, nWW, nHH, bmpWater.Canvas.Handle, 0, 0, SRCCOPY); nY := (nIH div 2) + (nIH div 10); _bmpWater.Canvas.Draw(nX, nY, _bmpWaterP); // c.Draw(nX, nY, bmpWater); // BitBlt(DC, nX, nY, nWW, nHH, bmpWater.Canvas.Handle, 0, 0, SRCCOPY); _bmpWater.TransparentColor := clWhite; _bmpWater.Transparent := true; end; if bStartPage then begin // 왜인지는 모르겠지만... 워터마크 크기에 +1을 안해주면 // FinePrint, HEC 보안프린터에서 이미지 출력이 되지 않는다...23_1017 10:56:48 kku nWW := _bmpWaterP.Width + 1; nHH := _bmpWaterP.Height + 1; // nWW := Round((nScalePercent / 100) * bmpWater.Width); // nHH := Round((nScalePercent / 100) * bmpWater.Height); nX := (nW div 2) - (nWW div 2); nY := (nH div 2) - (nH div 3); MemCanvas.StretchDraw(Rect(nX, nY, nX + nWW, nY + nHH), _bmpWaterP); nY := (nH div 2) + (nH div 10); MemCanvas.StretchDraw(Rect(nX, nY, nX + nWW, nY + nHH), _bmpWaterP); end else if _bmpWater <> nil then begin if gAppHook.Helper.IsExcel then MemCanvas.StretchDraw(Rect(0, 0, nW, nH), _bmpWater) else DrawBitmapWater(MemCanvas.Handle, 0, 0, _bmpWater); end; end; if bStartPage then BitBlt(DC, 0, 0, nW, nH, MemCanvas.Handle, 0, 0, SRCCOPY); finally SetTextColor(MemCanvas.Handle, oldColor); SelectObject(MemCanvas.Handle, hOldBmp); DeleteObject(hbmp); if bStartPage then // 필요없음 DeleteDC(MemCanvas.Handle); MemCanvas.Handle := 0; SelectObject(DC, hOldFont); end; gAppHook.Log(Format('ProcessWartermark_GEC() - Completed, LastError=%d', [GetLastError])); except // 실패하면 출력 안되게 해준다 22_0907 14:28:52 kku Result := false; DeleteDC(DC); end; finally gAppHook.Helper.bIsWaterMaking_ := false; RestoreDC(DC, -1); end; end; end.