在Windows中打印文件

时间:2018-07-28 21:37:31

标签: c winapi printing

说我有一个ASCII编码的文本文件mytextfile.txt,我想打印它。

我在MSDN上找到了各种来源,但似乎都没有用:

来自here

  

定义从Windows Forms应用程序打印时将输出发送到打印机的可重用对象。   public ref class PrintDocument : Component

但这仅适用于C ++(而非C)

我还发现this,它定义了几个功能,实际上似乎没有一个能够打印:

  

IPrintDocumentPackageStatusEvent:表示打印作业的进度。

     

IPrintDocumentPackageTarget:允许用户枚举受支持的包目标类型并创建具有给定类型ID的目标。 IPrintDocumentPackageTarget还支持跟踪包装打印进度和取消。

     

IPrintDocumentPackageTargetFactory:与IPrintDocumentPackageTarget一起用于启动打印作业。

啊!看起来像!事实证明,这也适用于C ++:

IPrintDocumentPackageTargetFactory::CreateDocumentPackageTargetForPrintJob

我认为尝试模仿C类是不切实际的(而且很困难)。

我可以使用print命令:

system("print mytextfile.txt");

但这似乎有点不合常理,是否有更好的方法来使用功能打印文件?

要澄清一下:我希望此打印在纸张而不是终端上。

2 个答案:

答案 0 :(得分:3)

以下是一些适合您的示例打印代码,可以正确执行此工作。这是一个最小的,独立的示例,您应该可以编译并运行它。基于此,您应该能够使其适应您的特定需求。首先,这里是代码,然后是一些解释性注释。

请注意,我已将此内容编写为控制台应用程序,但这可能并不是您真正想要的。另外,现在可以根据要求从C调用完成所有工作的函数(print_file()。 OK,这是代码:

#include <windows.h>
#include <conio.h>

#include <string>
#include <fstream>
#include <iostream>

#define SCALE_FACTOR        100     // percent

inline static int MM_TO_PIXELS (int mm, int dpi)
{
    return MulDiv (mm * 100, dpi, 2540);
}

// Calculate the wrapped height of a string
static int calculate_wrapped_string_height (HDC hDC, int width, const std::string& s)
{
    RECT r = { 0, 0, width, 16384 };
    DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK);
    return (r.bottom == 16384) ? calculate_wrapped_string_height (hDC, width, " ") : r.bottom;
}

// Print a string in the width provided.
static void print_string (HDC hDC, int x, int y, int width, const std::string& s)
{
    RECT r = { x, y, x + width, 16384 };
    DrawText (hDC, s.c_str (), (int) s.length (), &r, DT_NOPREFIX | DT_WORDBREAK);
}

// Print page number.  Returns (y + vertical space consumed)
static int print_pagenum (HDC hDC, int x, int y, int width, int& pagenum)
{
    std::string hdr = "Page: " + std::to_string (++pagenum) + "\n";
    int space_needed = calculate_wrapped_string_height (hDC, width, hdr);
    print_string (hDC, x, y, width, hdr);
    std::cout << "Printing page: " << pagenum << "\n";
    return space_needed;
}

extern "C" bool print_file (const char *filename)
{
    std::ifstream f;
    f.open ("g:\\temp\\print_me.txt", std::ios_base::in);

    if (!f)
    {
        std::cout << "Cannot open input file, error " << GetLastError () << "\n";
        return false;
    }

    // Display print dialog
    PRINTDLGEX pdex = { sizeof (pdex) };
    PRINTPAGERANGE pr [10] = { };
    HDC hDC;

    pdex.hwndOwner = GetDesktopWindow ();
    pdex.Flags = PD_ALLPAGES | PD_RETURNDC | PD_NOCURRENTPAGE | PD_NOSELECTION;
    pdex.nMaxPageRanges = _countof (pr);
    pdex.nPageRanges = 1;
    pr [0].nFromPage = pr [0].nToPage = 1;
    pdex.lpPageRanges = pr;
    pdex.nMinPage = 1;
    pdex.nMaxPage = 999999;
    pdex.nCopies = 1;
    pdex.nStartPage = START_PAGE_GENERAL;

    HRESULT hr = PrintDlgEx (&pdex);

    if (hr != S_OK)
    {
        std::cout << "PrintDlgEx failed, error " << GetLastError () << "\n";
        return false;
    }

    if (pdex.dwResultAction == PD_RESULT_CANCEL)
        return false;

    hDC = pdex.hDC;
    if (pdex.dwResultAction != PD_RESULT_PRINT)
    {
        DeleteDC (hDC);
        return false;
    }

    // Only print what we need to
    int max_page = 0x7fffffff;    
    if (pdex.Flags & PD_PAGENUMS)
    {
        max_page = 0;
        for (int i = 0; i < (int) pdex.nPageRanges; ++i)
        {
            if ((int) pdex.lpPageRanges [i].nToPage > max_page)
                max_page = pdex.lpPageRanges [i].nToPage;
        }
    }

    constexpr int dpi = 96 * 100 / SCALE_FACTOR;
    int lpx = GetDeviceCaps (hDC, LOGPIXELSX);
    int lpy = GetDeviceCaps (hDC, LOGPIXELSX);
    int res_x = GetDeviceCaps (hDC, HORZRES);
    int res_y = GetDeviceCaps (hDC, VERTRES);

    // margins    
    int left_margin = MM_TO_PIXELS (10, dpi);
    int top_margin = MM_TO_PIXELS (20, dpi);
    int right_margin = MM_TO_PIXELS (20, dpi);
    int bottom_margin = MM_TO_PIXELS (20, dpi);

    int width = MulDiv (res_x, dpi, lpx) - (left_margin + right_margin);
    int y_max = MulDiv (res_y, dpi, lpy) - bottom_margin;

    // Set up for SCALE_FACTOR
    SetMapMode (hDC, MM_ANISOTROPIC);
    SetWindowExtEx (hDC, dpi, dpi, NULL);
    SetViewportExtEx (hDC, lpx, lpy, NULL);
    SetStretchBltMode (hDC, HALFTONE);

    DOCINFO di = { 0 };
    di.cbSize = sizeof (di);
    di.lpszDocName = "Stack Overflow";
    int job_id = StartDoc (hDC, &di);

    if (job_id <= 0)
    {
        std::cout << "StartDoc failed, error " << GetLastError () << "\n";
        DeleteDC (hDC);
        return false;
    }

    SetBkMode (hDC, TRANSPARENT);
    LOGFONT lf = { 0 };
    lf.lfWeight = FW_NORMAL;
    lf.lfHeight = -12;
    HFONT hTextFont = CreateFontIndirect (&lf);
    HFONT hOldFont = (HFONT) GetCurrentObject (hDC, OBJ_FONT);
    SelectObject (hDC, hTextFont);

    int x = left_margin;
    int y = top_margin;
    int pagenum = 0;
    int err = StartPage (hDC);

    if (err <= 0)
    {
        std::cout << "StartPage failed, error " << GetLastError () << "\n";
        DeleteDC (hDC);
        return false;
    }

    y += print_pagenum (hDC, x, y, width, pagenum);

    // Printing loop, per line
    for ( ; ; )
    {
        if (_kbhit ())
        {
            AbortDoc (hDC);
            break;
        }

        std::string line;
        std::getline (f, line);
        if (!f)
            break;

        int space_needed = calculate_wrapped_string_height (hDC, width, line);
        if (space_needed > y_max - y)
        {
            if (pagenum >= max_page)
                break;

            if (EndPage (hDC) < 0 || StartPage (hDC) < 0)
                break;

            y = top_margin;
            y += print_pagenum (hDC, x, y, width, pagenum);
        }

        print_string (hDC, x, y, width, line);
        y += space_needed;
    }        

    EndPage (hDC);
    EndDoc (hDC);

    SelectObject (hDC, hOldFont);
    DeleteObject (hTextFont);
    DeleteDC (hDC);
    return true;
}

// main
int main ()
{
    bool ok = print_file ("g:\\temp\\print_me.txt");
    return !ok;
}

注释:

  1. 该代码显示如何正确分页输出。我在他的打印输出中包括页码,只是为了好玩。

  2. 输入文件名是硬编码的。请根据您的需求进行调整。

  3. 正如我所说,这是作为控制台应用程序编写的。如果要在Windows应用程序中包括此功能,则将使用其他父窗口句柄和其他机制(通常是无模式对话框)来报告进度并允许用户取消打印作业。

  4. 编写的代码期望被编译为ANSI(纯粹为了方便)。我确定您可以解决此问题。

  5. 此代码不能正确处理用户在Windows标准“打印”对话框中输入的页面范围。我把它留给读者练习。

  6. 要使此代码可从C调用,请将其编译为单独的.cpp文件(当然不包括main),然后将原型print_file(在 单独的.h文件)为:

    extern "C" bool print_file (const char *filename);

然后#将此文件同时包含在.cpp文件和.c文件中。

请注意,bool是预定义类型-在C99和更高版本中为-,请参见:

https://stackoverflow.com/a/1608350/5743288

答案 1 :(得分:1)

您可以读取一行,循环打印一行,直到EOF

编辑后输出到打印机

Xamarin.Forms.ContentPage