You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GDI+ DrawString打印PDF/PNG时无法绘制水印问题排查

问题排查:打印PDF/PNG时水印不显示,DOCX/JPG正常

Hook了EndPage()函数,使用GDI+的DrawString为打印的JPG、DOCX、PNG、PDF文档添加水印。DOCX和JPG格式下水印正常显示,但PDF和PNG格式完全看不到水印。已确认通过RPC获取的水印参数在所有格式下均正确,附上代码请求排查原因:

int EndPageHook::hookFunction(HDC hdc)
{


    //Intialize GdiplusStartupInput and Graphics
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
    Graphics graphic_Obj(hdc);

    //Get Watermark Information Throught RPC
    WatermarkParameters = WaterMarkRPCClient::GetWaterMarkInfo(status, printInfo);
    
    //Get Watermark Information and set in local variables
    string s = std::to_string(WatermarkParameters.transparency);
    s = std::to_string(WatermarkParameters.positions);
    s = std::to_string(WatermarkParameters.font);

    //Concatenation Text + User + Ip + Date 
    wcsncat_s(finalsenetce, WatermarkParameters._text, 512);

    if (WatermarkParameters.Has_UserName)
    {
        wcsncat_s(finalsenetce, WatermarkParameters._username, 256);
    }
    if (WatermarkParameters.Has_Client)
    {
        wcsncat_s(finalsenetce, WatermarkParameters._client, 256);
    }
    if (WatermarkParameters.Has_Date)
    {
        wcsncat_s(finalsenetce, WatermarkParameters._date, 256);
    }
    {
    
        //Set watermark position
        StringAlignment WMLocation{};
        StringFormat stringFormat;
        switch (WatermarkParameters.positions)
        {
        case 1:
            stringFormat.SetAlignment(StringAlignment::StringAlignmentNear);
            stringFormat.SetLineAlignment(StringAlignment::StringAlignmentNear);
            break;
        case 2:
            stringFormat.SetAlignment(StringAlignment::StringAlignmentCenter);
            stringFormat.SetLineAlignment(StringAlignment::StringAlignmentCenter);
            break;
        case 3:
            stringFormat.SetAlignment(StringAlignment::StringAlignmentFar);
            stringFormat.SetLineAlignment(StringAlignment::StringAlignmentFar);
            break;
        default:
            stringFormat.SetAlignment(StringAlignment::StringAlignmentCenter);
            stringFormat.SetLineAlignment(StringAlignment::StringAlignmentCenter);
            break;
        }


        
        // Claculate Page Size
        double WidthsPixels = GetDeviceCaps(hdc, VERTRES); 
        double   HeightsPixels = GetDeviceCaps(hdc, HORZRES);
        double WidthsPixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSX);
        double   HeightsPixelsPeInch = GetDeviceCaps(hdc, LOGPIXELSY);
        ////----------------------------------------------------------------------------------------------------------------    

        Gdiplus::FontFamily nameFontFamily(L"Arial");
        Gdiplus::Font         font(&nameFontFamily, WatermarkParameters.font, Gdiplus::FontStyleBold, Gdiplus::UnitPoint);
        Gdiplus::RectF        rectF(0, 0, 20, 20);
        Gdiplus::SolidBrush   solidBrush(Gdiplus::Color(WatermarkParameters.transparency, 250, 0, 250));

        rectF.Width = (HeightsPixels / HeightsPixelsPeInch)* Screendpi;
        rectF.Height = (WidthsPixels / WidthsPixelsPerInch)* Screendpi;
        


        graphic_Obj.DrawString(finalsenetce, -1, &font, rectF, &stringFormat, &solidBrush);
    }
    GdiplusShutdown(m_gdiplusToken); 

    return getOriginalFunction()(hdc);
}

可能的原因及修复方案

1. GDI+初始化/销毁时机错误

每次EndPageHook调用都重复执行GdiplusStartup和GdiplusShutdown,会导致GDI+状态异常,尤其在PDF/PNG这类依赖特殊打印驱动的场景下。正确做法是:

  • 在Hook初始化时仅调用一次GdiplusStartup
  • 在Hook卸载时仅调用一次GdiplusShutdown
  • 不要在每次EndPage回调内重复执行这两个操作

2. 页面尺寸参数搞反

代码中VERTRES和HORZRES的取值逻辑错误:

// 错误:VERTRES是垂直像素数(页面高度),HORZRES是水平像素数(页面宽度)
double WidthsPixels = GetDeviceCaps(hdc, VERTRES); 
double HeightsPixels = GetDeviceCaps(hdc, HORZRES);

这会导致rectF宽高计算完全颠倒,水印被绘制到页面可视区域外。修正为:

double pageWidthPixels = GetDeviceCaps(hdc, HORZRES);
double pageHeightPixels = GetDeviceCaps(hdc, VERTRES);

3. Screendpi变量未定义/取值错误

代码中使用的Screendpi未看到定义和赋值,若该值与当前打印DC的DPI不匹配,会导致rectF尺寸错误,水印超出页面范围。建议直接使用打印DC自身的DPI计算:

rectF.Width = pageWidthPixels;
rectF.Height = pageHeightPixels;

或确保Screendpi赋值为当前打印DC的LOGPIXELSX/LOGPIXELSY。

4. 目标字符串未初始化

wcsncat_s(finalsenetce, WatermarkParameters._text, 512)中,若finalsenetce未初始化为空字符串,首次拼接会因为缓冲区无\0终止符导致未定义行为,在PDF/PNG打印场景下可能直接绘制失败。需提前初始化:

wchar_t finalsenetce[1024] = {0}; // 分配足够大的缓冲区并清零

5. 字体加载失败

FontFamily nameFontFamily(L"Arial")若打印驱动中不存在Arial字体,会导致字体加载失败,DrawString无输出。可替换为系统内置、打印驱动必支持的字体,或增加可用性检查:

Gdiplus::FontFamily nameFontFamily(L"Arial");
if (!nameFontFamily.IsAvailable()) {
    // 字体不可用时 fallback 到默认字体
    nameFontFamily = Gdiplus::FontFamily(L"Segoe UI");
}

6. 打印DC兼容性问题

部分PDF/PNG打印驱动的DC可能不兼容GDI+操作,可尝试保存并恢复DC状态:

int savedDC = SaveDC(hdc);
// 执行GDI+绘制逻辑
graphic_Obj.DrawString(...);
RestoreDC(hdc, savedDC);

内容的提问来源于stack exchange,提问作者Mohammadreza Hojatoleslami

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:01:36