{*******************************************************} { } { ProcessWatermark } { } { Copyright (C) 2025 kku } { } {*******************************************************} unit ProcessWM_Def; interface uses Winapi.Windows, System.Classes, Vcl.Graphics; function ProcessWartermark(DC: HDC; bStartPage: Boolean = true): Boolean; implementation uses AppCtrlDefine, ApiHookPrint, BS1Hook, Tocsg.Safe, System.SysUtils, Condition, Tocsg.Graphic, Tocsg.Trace, EM.DelphiZXingQRCode, Winapi.GDIPAPI, Tocsg.Convert; function ProcessWartermark(DC: HDC; bStartPage: Boolean = true): Boolean; var nW, nH, i, nX, nY, nGapW, nGapH, nDefDivFont: Integer; sText: String; MemCanvas: TCanvas; hbmp, hOldBmp: HBITMAP; pen: HPEN; oldColor: TColor; begin Result := true; if _PrtDC <> DC then exit; SaveDC(DC); gAppHook.Helper.bIsWaterMaking_ := true; try // WriteLnFileEndUTF8('C:\ProgramData\HE\prt.log', 'Step-0'); try if gAppHook.Helper.IsExcel then begin // 클리핑 제거 25_0605 14:51:11 kku SelectClipRgn(DC, 0); // 엑셀은 출력시트 설정에 따라 전체 영역으로 잡히는 않는 현상이 있다.. 24_0805 13:46:31 kku nW := GetDeviceCaps(DC, HORZRES); // 세로모드 : 4961, 가로모드 : 7016, 크로미움 프린트 인쇄시 4760 nH := GetDeviceCaps(DC, VERTRES); // 세로모드 : 7016, 가로모드 : 4961, 크로미움 프린트 인쇄시 6814 end else begin // if bStartPage then // 이럴리 없겠지만 안전장치 // exit; var rc: TRect; ZeroMemory(@rc, SizeOf(rc)); GetClipBox(DC, rc); nW := rc.Width; nH := rc.Height; // LogToReg('Step-1', Format('W = %d, H = %d,', [nW, nH])); // LogToReg('Step-2', Format('W = %d, H = %d,', [GetDeviceCaps(DC, HORZRES), GetDeviceCaps(DC, VERTRES)])); if nW = 0 then nW := GetDeviceCaps(DC, HORZRES); // 세로모드 : 4961, 가로모드 : 7016, 크로미움 프린트 인쇄시 4760 if nH = 0 then nH := GetDeviceCaps(DC, VERTRES); // 세로모드 : 7016, 가로모드 : 4961, 크로미움 프린트 인쇄시 6814 end; // nX := GetDeviceCaps(DC, ASPECTX); // nY := GetDeviceCaps(DC, ASPECTY); // bStartPage := CompareText(gAppHook.ModuleName, 'notepad.exe') = 0; Guard(MemCanvas, TCanvas.Create); // if bStartPage then // begin // MemCanvas.Handle := CreateCompatibleDC(DC); // end else MemCanvas.Handle := DC; //CreateCompatibleDC(DC); hbmp := CreateCompatibleBitmap(MemCanvas.Handle, nW, nH); // hbmp := CreateCompatibleBitmap(DC, nW, nH); hOldBmp := SelectObject(MemCanvas.Handle, hbmp); // gAppHook.Log(Format('ProcessWartermark() - DocSize X=%d, Y=%d, W=%d, H=%d, dc=%d', [nX, nY, nW, nH, DC])); // LogToReg('Step0', Format('ProcessWartermark() - 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); // pen := CreatePen(PS_SOLID, 1, RGB(0, 255, 0)); // hOldBmp := SelectObject(MemCanvas.Handle, pen); nGapW := 0; nGapH := 0; // SetBkMode(MemCanvas.Handle, TRANSPARENT); // MemCanvas.Font.Orientation := 250; MemCanvas.Font.Name := 'Tahoma'; if gAppHook.Helper.bSmallFont_ then begin if Pos('PDF', UpperCase(_sPrtName)) = 0 then begin // 일반 프린터에서만 밀리는 현상이 있어서 갭을 준다.. 24_0627 10:52:33 kku nGapW := 19; nGapH := 17; end; nDefDivFont := 180; end else begin if gAppHook.Helper.bEndDocProc_ then begin if CompareText(gAppHook.ModuleName, 'winword.exe') = 0 then nGapH := 70; end; nDefDivFont := 190; end; // 기본 폰트 조절 if nW > nH then MemCanvas.Font.Size := nW div 130 else MemCanvas.Font.Size := nH div 130; oldColor := GetTextColor(DC); // 폰트색 지정... 프로그램에 따라 지정되는 설정이 다르다 MemCanvas.Font.Color := clSilver; // 메모장등 SetTextColor(DC, clSilver); // 엑셀등 try sText := gAppHook.Helper.sPrintWaterTxt; SetBkMode(MemCanvas.Handle, TRANSPARENT); case gAppHook.Helper.CtrlOpt.dwCustomerType of CUSTOMER_UNITUS, CUSTOMER_MOTRAS, CUSTOMER_INZENT : MemCanvas.Font.Size := MemCanvas.Font.Size - 2; // 폰트 크기 줄임 24_0619 14:23:38 kku CUSTOMER_SOLIDEO : begin // MemCanvas.Font.Color := $969696; // SetTextColor(DC, $969696); // MemCanvas.Font.Size := MemCanvas.Font.Size - 17; MemCanvas.Font.Color := clGray; // 메모장등 SetTextColor(DC, clGray); // 엑셀등 MemCanvas.Font.Size := MemCanvas.Font.Size - 9; Dec(nGapH, 7); end; end; if gAppHook.Helper.CtrlOpt.dwCustomerType = CUSTOMER_DEMO then begin nGapH := 0; // QR 코드 추가 25_1106 20:17:25 kku var QRCode: TDelphiZXingQRCode; Guard(QRCode, TDelphiZXingQRCode.Create); QRCode.Data := sText; QRCode.QuietZone := 4; var QrBmp: TBitmap; Guard(QrBmp, TBitmap.Create); QrBmp.SetSize(QRCode.Rows, QRCode.Columns); var R, C, nQrSize: Integer; for R := 0 to QRCode.Rows - 1 do begin for C := 0 to QRCode.Columns - 1 do begin if (QRCode.IsBlack[R, C]) then begin QrBmp.Canvas.Pixels[C, R] := clBlack; end else begin QrBmp.Canvas.Pixels[C, R] := clWhite; end; end; end; var rc: TRect; if nW < nH then nQrSize := nW div 20 else nQrSize := nH div 20; rc.Left := nGapW + nW - nQrSize; rc.Right := nGapW + nW; rc.Top := nGapH + nH - nQrSize; rc.Bottom := nGapH + nH; MemCanvas.StretchDraw(rc, QrBmp); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if _bmpWater = nil then begin var sImgPath: String := ExtractFilePath(gAppHook.Helper.DllPath) + 'CI.bmp'; if FileExists(sImgPath) then begin try _bmpWater := TBitmap.Create; _bmpWater.PixelFormat := pf4bit; // 이거 켜면 흑백 출력시 안됨 24_0807 15:52:56 kku _bmpWater.LoadFromFile(sImgPath); // LogToReg('BW-01', BooleanToStr(gAppHook.Helper.CurAppType = catWebb, 'YES', 'NO')); if gAppHook.Helper.CurAppType = catWebb then ScalePercentBmp(_bmpWater, 40) else ScalePercentBmp(_bmpWater, 140); _bmpWater.TransparentColor := clWhite; _bmpWater.Transparent := true; // _bmpWater.SaveToFile('C:\Users\kku\Desktop\이전 바탕화면\출력 추출 데이터\1.bmp'); except // .. end; end; end; if _bmpWater <> nil then begin var cTrMatrix: TColorMatrix; ZeroMemory(@cTrMatrix, SizeOf(cTrMatrix)); if gAppHook.Helper.bSmallFont_ then cTrMatrix := MakeColorMatrix(0.2, 0.2, 0.2, BooleanToFloat(gAppHook.Helper.CtrlOpt.fWmTran <> 0.0, gAppHook.Helper.CtrlOpt.fWmTran, 0.05)) else // cTrMatrix := MakeColorMatrix(0.2, 0.2, 0.2, 0.0099); cTrMatrix := MakeColorMatrix(0.2, 0.2, 0.2, BooleanToFloat(gAppHook.Helper.CtrlOpt.fWmTran <> 0.0, gAppHook.Helper.CtrlOpt.fWmTran, 0.06)); Dec(nW, nGapW); Dec(nH, nGapH); nX := (nW div 4) - (_bmpWater.Width div 2); nY := (nH div 4) - (_bmpWater.Height div 2); // MemCanvas.Draw(nX, nY, _bmpWater); if not gAppHook.Helper.IsExcel or ( gAppHook.Helper.IsExcel and bStartPage and not _bDoStartProc) or ( gAppHook.Helper.IsExcel and not bStartPage ) then begin DrawBitmapWaterEx(MemCanvas.Handle, nX, nY, _bmpWater, @cTrMatrix); end; end; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< end else begin MemCanvas.TextOut(nW - MemCanvas.TextWidth(sText) + nGapW, nH - MemCanvas.TextHeight(sText) + nGapH, sText); end; case gAppHook.Helper.CtrlOpt.dwCustomerType of CUSTOMER_DEMO, CUSTOMER_INZENT, CUSTOMER_SOLIDEO, CUSTOMER_CJONS : ; else begin sText := gAppHook.Helper.sCurDocName_; MemCanvas.TextOut(nGapW, nH - MemCanvas.TextHeight(sText) + nGapH, sText); end; end; // if bStartPage then // BitBlt(DC, 0, 0, nW, nH, MemCanvas.Handle, 0, 0, SRCCOPY); SelectObject(MemCanvas.Handle, hOldBmp); // DeleteObject(pen); DeleteObject(hbmp); // if bStartPage then // DeleteDC(MemCanvas.Handle); MemCanvas.Handle := 0; finally SetTextColor(DC, oldColor); end; gAppHook.Log(Format('ProcessWartermark() - 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; function ProcessWartermark_Old(DC: HDC; bStartPage: Boolean = true): Boolean; var nW, nH, nWW, nHH, i, nGapH: Integer; sOut: String; MemCanvas: TCanvas; hbmp, hOldBmp: HBITMAP; bmpText: TBitmap; begin Result := true; // 엑셀 (excel.exe)의 경우 이렇게 안하면 다음 페이지로 밀리는 현상 있음. // 아마도 DC를 별도 추가 처리 하는 듯 함 23_1024 11:00:13 kku if _PrtDC <> DC then exit; gAppHook.Helper.bIsWaterMaking_ := true; try bStartPage := false; try nW := GetDeviceCaps(DC, HORZRES); nH := GetDeviceCaps(DC, VERTRES); Guard(MemCanvas, TCanvas.Create); if bStartPage then MemCanvas.Handle := CreateCompatibleDC(DC) else MemCanvas.Handle := DC; //CreateCompatibleDC(DC); hbmp := CreateCompatibleBitmap(DC, nW, nH); hOldBmp := SelectObject(MemCanvas.Handle, hbmp); gAppHook.Log(Format('ProcessWartermark() - DocSize W=%d, H=%d, dc=%d', [nW, nH, DC])); sOut := gAppHook.Helper.sPrintWaterTxt; if _nFontSize = 0 then begin _nFontSize := MemCanvas.Font.Size; while True do begin if _nFontSize > 200 then break; if MemCanvas.TextHeight(sOut) > 60 then begin Dec(_nFontSize); break; end; Inc(_nFontSize); MemCanvas.Font.Size := _nFontSize; end; end; if _bmpWater = nil then begin // for i := 0 to 3 do sOut := sOut + WORD_GAP + sOut; _bmpWater := TBitmap.Create; _bmpWater.PixelFormat := pf4bit; // _bmpWater.PixelFormat := pf32bit;// pf4bit; nWW := nW div 4; nHH := nH div 4; _bmpWater.SetSize(nWW, nHH); _bmpWater.Canvas.Font.Assign(MemCanvas.Font); // bmpText.Canvas.Font.Color := clSilver; _bmpWater.Canvas.Font.Color := clGray; //$F1F1F1; // ; // ; _bmpWater.Canvas.Font.Size := _nFontSize; _bmpWater.Canvas.Font.Style := _bmpWater.Canvas.Font.Style + [fsBold]; _bmpWater.Canvas.FillRect(Rect(0, 0, nWW, nHH)); _bmpWater.TransparentColor := clWhite; _bmpWater.Transparent := true; Guard(bmpText, TBitmap.Create); bmpText.PixelFormat := pf4bit; // bmpText.PixelFormat := pf32bit;// pf4bit; bmpText.Canvas.Font.Assign(MemCanvas.Font); // bmpText.Canvas.Font.Color := clSilver; bmpText.Canvas.Font.Color := clSilver;//$F1F1F1; bmpText.Canvas.Font.Size := _nFontSize; bmpText.Canvas.Font.Style := bmpText.Canvas.Font.Style + [fsBold]; // bmpText.Canvas.Font.Orientation := 250; bmpText.SetSize(_bmpWater.Canvas.TextWidth(sOut), _bmpWater.Canvas.TextHeight(sOut)); bmpText.Canvas.Brush.Color := clWhite; bmpText.Canvas.Brush.Style := bsSolid; // SetBkMode(bmpText.Canvas.Handle, TRANSPARENT); // var textColor: COLORREF := RGB(255, 0, 0); // var textAlpha: Integer := 128; // textColor := textColor and ($00FFFFFF or textAlpha shl 24); // SetTextColor(bmpText.Canvas.Handle, textColor); bmpText.Canvas.TextOut(0, 0, sOut); var ii: Integer; var jj: Integer; for ii := 0 to bmpText.Width - 1 do for jj := 0 to bmpText.Height - 1 do if bmpText.Canvas.Pixels[ii, jj] <> clWhite then if ((ii + jj) mod 2) = 0 then bmpText.Canvas.Pixels[ii, jj] := clWhite else bmpText.Canvas.Pixels[ii, jj] := clGray; // bmpText.Canvas.Pixels[ii, jj] := bmpText.Canvas.Pixels[ii, jj] and $00FFFFFF or textAlpha shl 24; RotateBitmap_PlgBlt(bmpText, -0.45, true, clWhite); // bmpText.Canvas.FillRect(Rect(0, 0, nWW, nHH)); // bmpText.TransparentColor := clWhite; // bmpText.Transparent := true; // if bStartPage then // PatBlt(MemCanvas.Handle, 0, 0, nW, nH, WHITENESS); nGapH := bmpText.Height; // nHH div (nRepeat - 1); i := (nGapH div 2) * -1; while i < nHH + nGapH do begin _bmpWater.Canvas.Draw(0, i, bmpText); Inc(i, nGapH); end; // bmpText.Canvas.TextOut(0, bmpText.Height - (bmpText.Height div 3), sOut); end; if _bmpWater <> nil then MemCanvas.StretchDraw(Rect(0, 0, nW, nH), _bmpWater); DeleteObject(hbmp); if bStartPage then DeleteDC(MemCanvas.Handle); MemCanvas.Handle := 0; gAppHook.Log(Format('ProcessWartermark() - Completed, LastError=%d', [GetLastError])); except // 실패하면 출력 안되게 해준다 22_0907 14:28:52 kku Result := false; DeleteDC(DC); end; finally gAppHook.Helper.bIsWaterMaking_ := false; end; end; end.