【问题标题】:image created on Cimg display different on a pdf when saved using GDI+ with pdf created with jagPDF使用 GDI+ 保存时,在 Cimg 上创建的图像在 pdf 上显示不同,而使用 jagPDF 创建的 pdf
【发布时间】:2016-09-28 02:20:08
【问题描述】:

我需要做的很简单,我需要使用 CIMG 绘制一个矢量,然后将图形保存为 jpg 并使用 JAGPDF 将 jpg 添加到 PDF 文档中。为了将 CIMG 保存为 JPG,程序使用了一个名为 Image Magick 的外部程序。

我想避免使用该程序并使用GDI+,而是首先将 CIMG 保存为 BMP(它本身就是这样做的),然后从 bmp 中保存 jpg。

MCVE 程序如下所示

#include "CImg.h"
#include <jagpdf/api.h>
#include <vector>

using namespace jag;
using namespace cimg_library;

int main(int argc, char** const argv)
{

    const float x0 = 0;
    const float x1 = 9;
    const int resolution = 5000;

    // Create plot data.
    CImg<double> values(1, resolution, 1, 1, 0);

    const unsigned int r = resolution - 1;

    for (int i1 = 0; i1 < resolution; ++i1)
    {
        double xtime = x0 + i1*(x1 - x0) / r;
        values(0, i1) = 2 * sin(xtime);
    }

    CImg<unsigned char> graph;
    graph.assign(750, 240, 1, 3, 255);

    static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 };
    static const unsigned char red[] = { 255, 200, 200 }, bred[] = { 255, 0, 0 };

    graph.draw_grid(6, 6, 0, 0, false, true, red, 10.0f, 0xFFFFFFFF, 0xFFFFFFFF);
    graph.draw_grid(30, 30, 0, 0, false, true, bred, 10.0f, 0xFFFFFFFF, 0xFFFFFFFF);

    graph.draw_graph(values, black, 1, 1, 1, 2, -2, 0xFFFFFFFF);;

    //////////////Method 1: Using Image Magick////////////////
    graph.save_jpeg("plot2.jpg");

    pdf::Document doc(pdf::create_file("report.pdf"));
    doc.page_start(848.68, 597.6);
    pdf::Image imag2 = doc.image_load_file("plot2.jpg");
    doc.page().canvas().image(imag2, 50, 50);
    doc.page_end();
    doc.finalize();
    //////////////Method 2: Using GDI+////////////////
    graph.save("plot.bmp");
    SaveFile();
    pdf::Document doc2(pdf::create_file("report2.pdf"));
    doc2.page_start(848.68, 597.6);
    pdf::Image imag = doc2.image_load_file("plot.jpg");
    doc2.page().canvas().image(imag, 50, 50);
    doc2.page_end();
    doc2.finalize();

    return 0;
}

SaveFile() 是使用 GDI+ 将 plot.bmp 转换为 plot.jpg 的以下函数

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include "GdiplusHelperFunctions.h"


#pragma comment (lib,"Gdiplus.lib")

VOID SaveFile()
{
    // Initialize GDI+.
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    CLSID   encoderClsid;
    Status  stat;
    Image*   image = new Gdiplus::Image(L"plot.bmp");

    // Get the CLSID of the PNG encoder.
    GetEncoderClsid(L"image/jpeg", &encoderClsid);

    stat = image->Save(L"plot.jpg", &encoderClsid, NULL);

    if (stat == Ok)
        printf("plot.jpg was saved successfully\n");
    else
        printf("Failure: stat = %d\n", stat);

    delete image;
    GdiplusShutdown(gdiplusToken);
    }

这两种方法保存的 jpgs 在属性中似乎具有相同的大小,但第一种方法将图像正确放入 pdf 中,而第二种方法将巨大的图像放入 pdf 中,即使它们的大小相同。我该如何解决这个问题?

附上report1和report2的截图

解决方案

根据您的建议,我能够修改 SaveFile 函数以便能够控制 de DPI,我发布新代码以防有人需要。

VOID SaveFile()
{
    // Initialize GDI+.
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    CLSID   encoderClsid;
    Status  stat;
    EncoderParameters encoderParameters;
    ULONG    quality;

    Gdiplus::Bitmap*   bitmap = new Gdiplus::Bitmap(L"plot.bmp");
    Gdiplus::REAL dpi = 96;
    bitmap->SetResolution(dpi,dpi);


    // Get the CLSID of the PNG encoder.
    GetEncoderClsid(L"image/jpeg", &encoderClsid);

    encoderParameters.Count = 1;
    encoderParameters.Parameter[0].Guid = EncoderQuality;
    encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
    encoderParameters.Parameter[0].NumberOfValues = 1;

   quality = 100;
   encoderParameters.Parameter[0].Value = &quality;

   stat = bitmap->Save(L"plot.jpg", &encoderClsid, &encoderParameters);


   if (stat == Ok)
        printf("plot.jpg was saved successfully\n");
   else
        printf("Failure: stat = %d\n", stat);

   delete bitmap;
   GdiplusShutdown(gdiplusToken);
   return;
}

【问题讨论】:

    标签: c++ pdf jpeg gdi+ cimg


    【解决方案1】:

    我猜 ImageMagick 包含一些过滤图像以适应画布的额外功能。聪明人。 在导出为 JPEG 之前,我会尝试调整图像大小。你可以去this guide。它基本上说您可以调整 bmp 的大小(在示例中它检查 w/h 比率但很好......)。目标应该是指定画布所需的大小。

    Gdiplus::Bitmap* GDIPlusImageProcessor::ResizeClone(Bitmap *bmp, INT width, INT height)
    {
        UINT o_height = bmp->GetHeight();
        UINT o_width = bmp->GetWidth();
        INT n_width = width;
        INT n_height = height;
        double ratio = ((double)o_width) / ((double)o_height);
        if (o_width > o_height) {
            // Resize down by width
            n_height = static_cast<UINT>(((double)n_width) / ratio);
        } else {
            n_width = static_cast<UINT>(n_height * ratio);
        }
        Gdiplus::Bitmap* newBitmap = new Gdiplus::Bitmap(n_width, n_height, bmp->GetPixelFormat());
        Gdiplus::Graphics graphics(newBitmap);
        graphics.DrawImage(bmp, 0, 0, n_width, n_height);
        return newBitmap;
    }
    

    然后,使用编码器保存它。此外,您还想检查是否可能需要使用编码器参数设置生成的 JPEG 的质量,如官方 documentation 所示。

    // Get the CLSID of the JPEG encoder.
       GetEncoderClsid(L"image/jpeg", &encoderClsid);
    
       // Before we call Image::Save, we must initialize an
       // EncoderParameters object. The EncoderParameters object
       // has an array of EncoderParameter objects. In this
       // case, there is only one EncoderParameter object in the array.
       // The one EncoderParameter object has an array of values.
       // In this case, there is only one value (of type ULONG)
       // in the array. We will let this value vary from 0 to 100.
    
       encoderParameters.Count = 1;
       encoderParameters.Parameter[0].Guid = EncoderQuality;
       encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
       encoderParameters.Parameter[0].NumberOfValues = 1;
    
       // Save the image as a JPEG with quality level 0.
       quality = 0;
       encoderParameters.Parameter[0].Value = &quality;
       stat = image->Save(L"Shapes001.jpg", &encoderClsid, &encoderParameters);
    
       if(stat == Ok)
          wprintf(L"%s saved successfully.\n", L"Shapes001.jpg");
       else
          wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes001.jpg");
    
       // Save the image as a JPEG with quality level 50.
       quality = 50;
       encoderParameters.Parameter[0].Value = &quality;
       stat = image->Save(L"Shapes050.jpg", &encoderClsid, &encoderParameters);
    
       if(stat == Ok)
          wprintf(L"%s saved successfully.\n", L"Shapes050.jpg");
       else
          wprintf(L"%d  Attempt to save %s failed.\n", stat, L"Shapes050.jpg");
    

    编辑:JAGPDF 还 says 绘画时考虑了图像 DPI。所以我们可能走在正确的道路上。

    假设我们想用我们的图像平铺页面的一个区域。 为此,我们需要知道图像尺寸。因为 width() 和 width() 以像素为单位返回大小,我们需要重新计算这些给用户 空间单位。 将图像 DPI 考虑在内将图像绘制到 帆布。图像通常指定其 DPI。如果不是这样的值 使用 images.default_dpi

    img_width = img.width() / img.dpi_x() * 72
    img_height = img.height() / img.dpi_y() * 72
    for x in range(7):
        for y in range(15):
            canvas.image(img, 90 + x * img_width, 100 + y * img_height)
    

    您可以尝试使用此SO answer 更改 DPI。

    【讨论】:

    • 非常感谢您的帮助,我终于可以根据您的建议更改 DPI 设置了。
    【解决方案2】:

    如果我正确理解您的问题,您的目标是消除对 ImageMagick 的依赖。

    您可以通过告诉 CImg 使用其对 JPEG 的内置支持来更简单地做到这一点。你需要做的就是

    • 定义 cimg_use_jpeg
    • 与 libjpeg 链接

    所以你的编译命令变成:

    g++ -Dcimg_use_jpeg ... -ljpeg
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-23
      • 1970-01-01
      • 2014-01-24
      • 2018-08-21
      • 2013-01-01
      • 2020-07-11
      • 2017-02-07
      • 2013-03-19
      相关资源
      最近更新 更多