发布者:
2010 年 4 月 18 日 (最后更新:2010 年 4 月 18 日)

打印机流

评分:3.8/5 (90 票)
*****
不久前,有人发了类似这样的帖子
1
2
ostream printer;
printer << "Some text" << endl;

并且疑惑为什么它没有打印出来。

所以我想写一个打印机流(同时也学习如何处理流操纵器)。

这是“概念验证”代码。
大多数注释已被删除,以便将 pstream.cpp 文件放在一个帖子里。
仅适用于 Windows。
这是一个可用的版本 - 但显然还没有完成。

它是用于打印文本(和文本文件)的 - 所以不要尝试打印二进制文件,如 Word 文件或 PDF 文件。 :-)


请享用。
pstream 头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#ifndef P_STREAM_H
#define P_STREAM_H

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



namespace pst
{
   
    //stream status
    const int  GOOD = 0x0000;
    const int  FAIL = 0x0001;

    //margins ninimum values
    const int MARGINMIN = 3; //millimetres



    template <typename T> class pstreamManip;

    /*!
    \brief class pstream 
    */
    class pstream
    {

    public:
        pstream();
        pstream(std::string whichPrinter);
        ~pstream();

        operator bool()
        {
            return !streamStatus;
        }

        
        void setTitle(std::string newTitle) { title = newTitle; }
        void setPageNumbers(bool bVal)      { pageNumbers = bVal;}
        void setTabSize(int newTabSize)     { tabSize = newTabSize;}
        
        /*!
        The following are member functions NOT manipulators
        */
        void setLeftMargin(int newMargin)   {leftMargin = newMargin <= MARGINMIN? MARGINMIN:newMargin; }
        void setTopMargin(int newMargin)    {topMargin = newMargin <= MARGINMIN? MARGINMIN:newMargin; };
        void setRightMargin(int newMargin)   {rightMargin = newMargin <= MARGINMIN? MARGINMIN:newMargin; };
        void setBottomMargin(int newMargin)   {bottomMargin = newMargin <= MARGINMIN? MARGINMIN:newMargin; };

        
        
        //Insertion Overloads
        //Add a string
        pstream& operator << (std::string name);
        //Add a char buffer
        pstream& operator << (char* name);
        //Add a file
        pstream& operator << (std::ifstream & inFile);

        //other insertion overlaods
        pstream& operator << (pstream & (*ptr)(pstream&)); //manipulator with no parameters
        /*!
        \brief Insertion overload for with manipulators with one parameter
        Note this is a template function so we put the definition here - much easier
        */
        template <typename T>
        pstream& operator << (pstreamManip<T> manip)
        {
            return manip(*this);
        }

        /*!
        \brief Useful static functions
        */
        static std::vector<std::string> getPrinterNames();
        

    private:
        std::string defaultPrinter;
        int streamStatus;
        std::vector<std::string> lines;
        int leftMargin;
        int rightMargin;
        int topMargin;
        int bottomMargin;
        std::string title;
        bool pageNumbers;
        int tabSize;

        
        //Copy Constructor - private and no body
        pstream(const pstream& other);

        
        /*!
        \brief manipulators
        These are mostly friends of pstream.
        They are in the pst namespace
        */
        
        /*!
        \brief Manipulators taking no arguments
        */
        friend pstream& flushp (pstream& p);
        /*!
        \brief manipulators taking one or more arguments
        These are structure based
        */
        
        template <typename T>
        friend pstreamManip<T> setLeftMargin (T margin);

        template <typename T>
        friend pstreamManip<T> setRightMargin (T margin);

        template <typename T>
        friend pstreamManip<T> setTopMargin (T margin);

        template <typename T>
        friend pstreamManip<T> setBottomMargin (T margin);

        /*!
        \brief private  functions for the manipulator(s) with arguments.
        \note These are static.
        */
        static pstream& _setMarginL(pstream & p, int n);
        static pstream& _setMarginR(pstream & p, int n);
        static pstream& _setMarginT(pstream & p, int n);
        static pstream& _setMarginB(pstream & p, int n);

    };//class pstream


    /*!
    \brief The manipulator class for manipulator with one parameter
    */
    template <typename T>
    class pstreamManip 
    {
    public:
        pstreamManip(pstream& (*fp)(pstream&, T val), T arg) : pf(fp), argValue(arg){};
        pstream& operator() (pstream & ps)
        { 
            return (*pf)(ps, argValue);
        }

    private:
        pstream& (*pf)(pstream&, T);
        T argValue;

    }; //class pstream

    /*!
    \brief All manipulators are in the pst namespace
    */
    pstream& flushp (pstream& p);

    
    template <typename T> 
    pstreamManip<T> setLeftMargin (T margin)
    {
        return pstreamManip<T>(pstream::_setMarginL, margin);
    }

    template <typename T> 
    pstreamManip<T> setRightMargin (T margin)
    {
        return pstreamManip<T>(pstream::_setMarginR, margin);
    }

    template <typename T> 
    pstreamManip<T> setTopMargin (T margin)
    {
        return pstreamManip<T>(pstream::_setMarginT, margin);
    }

    template <typename T> 
    pstreamManip<T> setBottomMargin (T margin)
    {
        return pstreamManip<T>(pstream::_setMarginB, margin);
    }


} //namespace pst




#endif 

pstream 实现文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#undef  UNICODE
#undef _UNICODE

#include "pstream.h"

#include <windows.h>
#include <tchar.h>
#include <iostream>

namespace pst
{

    pstream::pstream() :leftMargin(),rightMargin(),topMargin(),bottomMargin(),tabSize(4),pageNumbers(false)
    {
        DWORD buffSize;
        _TCHAR* pPrinterName;
        GetDefaultPrinter(0, &buffSize);
        pPrinterName = new _TCHAR[buffSize]();
        GetDefaultPrinter (pPrinterName,&buffSize);
        defaultPrinter =  pPrinterName;
        //Set the stream status
        streamStatus = (defaultPrinter == "")? FAIL : GOOD;
    }

    
    pstream::pstream (std::string whichPrinter) :leftMargin(), rightMargin(), topMargin (), bottomMargin(), tabSize(4), pageNumbers(false)
   {
       defaultPrinter = whichPrinter;
       streamStatus = (defaultPrinter == "")? FAIL : GOOD;     
   }

    
    
    pstream::~pstream()
    {

    }


    pstream& pstream::operator <<(std::string name)
    {
        if (! streamStatus)
        {
            //replace all tabs by the required number of spaces
            size_t position =0;

            while (position != std::string::npos)
            {
                
                if (   (position = name.find('\t', position)     ) != std::string::npos    )
                {
                    name.replace(position,1,std::string(tabSize,' ') );
                }

            }

            //We will split the string up at the new line char

            position = 0;
            size_t foundPos =0;

            do
            {
                foundPos = name.find('\n', position);
                std::string str = name.substr(position,foundPos-position);
                lines.push_back(str);
                position = foundPos+1;
            }while (position != 0);
        }
        
        return *this; 
    }   
    
    pstream& pstream::operator <<(char * name)
    {
        if (! streamStatus)
            operator<< (std::string(name));

        return *this;
    }


    pstream& pstream::operator << (std::ifstream & inFile)
    {
        if (!streamStatus)
        {
            if (inFile)
            {
                while (inFile)
                {
                    std::string str;
                    std::getline(inFile,str);

                    *this  << str ;
                }
            }
        }
        return *this;
    }



    pstream& pstream::operator <<(pstream &(*ptr)(pstream &))
    {

        ptr(*this);

        return *this;

    }


    pstream& flushp (pstream& p)
    {
        
        if (!p.lines.size() ||  p.streamStatus )
        {
            return p;
        }
        HDC pdc;

        pdc = CreateDC("winspool",p.defaultPrinter.c_str(),NULL, NULL);

        int logPixelsX, logPixelsY, vRes, hRes, scaleX;

        logPixelsX = GetDeviceCaps(pdc,LOGPIXELSX);
        logPixelsY = GetDeviceCaps(pdc, LOGPIXELSY);
        vRes = GetDeviceCaps(pdc, VERTRES);
        hRes = GetDeviceCaps(pdc, HORZRES);
        scaleX = GetDeviceCaps(pdc,SCALINGFACTORX);

        TEXTMETRIC tm;
        GetTextMetrics(pdc, &tm);
        long aveCharWidth=tm.tmAveCharWidth;
        long maxCharWidth=tm.tmMaxCharWidth;
        long charHeight =tm.tmHeight;
        long lineHeight = charHeight;// + tm.tmExternalLeading;

        //we need to adjust for the margins (change mm to pixels)
        int hPixelsMM = logPixelsX/25;
        int vPixelsMM = logPixelsY/25;
        int leftStart = ( (p.leftMargin*hPixelsMM) > hRes/4)? hRes/4: p.leftMargin*hPixelsMM;
        int rightEnd =  ( (p.rightMargin*hPixelsMM) > hRes/4)? hRes - hRes/4: hRes - p.rightMargin*hPixelsMM;
        int topStart = ( (p.topMargin*vPixelsMM) > vRes/4)? vRes/4: p.topMargin*vPixelsMM;
        int bottomEnd = ( (p.bottomMargin *vPixelsMM) > vRes/4)? vRes - vRes/4: vRes - p.bottomMargin*vPixelsMM;

        //Use 10 point courier font.
        HFONT hFont = CreateFont(-(logPixelsY/72)*10,0,0,0,0,0,0,0,0,0,0,0,FIXED_PITCH,"courier");
        HFONT hOldFont =(HFONT)SelectObject(pdc,hFont);
        

        //start document
        DOCINFO docInfo = {0};
        docInfo.cbSize = sizeof(docInfo);

        StartDoc(pdc,&docInfo);

        //start the pages
        int curPosY = topStart+lineHeight; 
        int curPosX= leftStart;
        int lastSpacePos; //used for word breaking checks

        std::vector<std::string>::iterator iter = p.lines.begin(), iterEnd = p.lines.end();

        StartPage(pdc); //Start the first page
        
        for (; iter != iterEnd ; ++iter )
        {
            std::string curStr = *iter;
            std::string tempStr;
            
            //So for empty lines we will  make the line one space long.
            if(curStr.length() ==0)
                curStr = " ";
            lastSpacePos = 0;
            for (int startIndex = 0,count =0; startIndex < curStr.length() ; count ++)
            {

                int charWidth;
                char c = curStr[count];
                GetCharWidth32(pdc, curStr[count],curStr[count],&charWidth);

                if (count < curStr.length())
                {
                    if(c ==' ')
                        lastSpacePos = count;

                    //check if the character will take us past the right hannd side margin
                    if( (curPosX += charWidth) > rightEnd)
                    {
                        if(lastSpacePos ==startIndex)
                        {
                            lastSpacePos = count-2; //fake a break
                        }
                        tempStr = curStr.substr(startIndex, lastSpacePos-startIndex);
                        count = lastSpacePos;//move the char indexer back to the last space position
                        startIndex = lastSpacePos;
                        //print the line 

                        TextOut(pdc,leftStart,curPosY,tempStr.c_str(), tempStr.length() );

                    }
                    else //No we won't go past RHS margin
                    {
                        continue;              
                    }

                }
                else
                {

                    tempStr = curStr.substr(startIndex, (count)-startIndex);
                    //print
                    TextOut(pdc,leftStart,curPosY,tempStr.c_str(), tempStr.length() );

                    startIndex = count;
                }

                //calculate the next print position - start new page if req'd
                curPosX = leftStart;
                curPosY += lineHeight;
                if (curPosY > bottomEnd)
                {
                    EndPage(pdc);
                    curPosY = topStart+lineHeight;
                    StartPage(pdc);
                }

            }  

        }
    
        EndDoc(pdc);
        p.lines.clear();
        //cleanup
        SelectObject(pdc, hOldFont);
        DeleteObject(hFont);
        DeleteDC(pdc);
        return p;
    }
   
    pstream& pstream:: _setMarginL(pstream & p, int margin)
    {
        p.setLeftMargin(margin);
        return p;
    }  
     
     pstream& pstream:: _setMarginR(pstream & p, int margin)
    {
        p.setRightMargin(margin);
        return p;
    }

    pstream& pstream:: _setMarginT(pstream & p, int margin)
    {
        p.setTopMargin(margin);
        return p;
    }

    pstream& pstream:: _setMarginB(pstream & p, int margin)
    {
        p.setBottomMargin(margin);
        return p;
    }  
        
    std::vector< std::string>  pstream::getPrinterNames()
    {

        LPBYTE buff = 0;
        DWORD buffSize =0;
        DWORD bytesNeeded;
        DWORD numPrinters=0;
        PRINTER_INFO_4 *pInfo;
        std::vector<std::string> names;

        if ( EnumPrinters(PRINTER_ENUM_LOCAL,NULL,4,
                            buff,0,&bytesNeeded,
                            &numPrinters) == FALSE  && bytesNeeded > 0)
        {
            buff = new byte[bytesNeeded];
            EnumPrinters(PRINTER_ENUM_LOCAL,NULL,4,buff,bytesNeeded,&bytesNeeded,&numPrinters);

        }

        pInfo = (PRINTER_INFO_4*)buff;
        for (int count =0; count < numPrinters; count ++)
        {
            names.push_back( std::string (pInfo->pPrinterName));
            ++pInfo;          
        }
   
        delete [] buff;

        return names;

    }

}//namespace pst 

用于测试的主程序文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// printer_stream.cpp : Defines the entry point for the console application.


#include "pstream.h"
#include <iostream>
#include <fstream>
#include <windows.h>
#include <string>
#include <iomanip>
using namespace std;




int main(int argc, char* argv[])
{
     
    //The stream has a static function that will  give  the names of the local printers attached to the system;
    vector<string> printerNames = pst::pstream::getPrinterNames();
    vector<string>::iterator itr;
    for (itr = printerNames.begin(); itr != printerNames.end(); ++ itr)
    {
        cout << *itr << '\n';
    }

    
    //Create a stream - default constructor will use the default ptinter
    pst::pstream p;

    //Set the page margins using the printer stream manipulators
    p << pst::setLeftMargin(10);
    p << pst::setTopMargin(20);
    p << pst::setRightMargin(30);
    p << pst::setBottomMargin(-50); //A zero or negative value will clamp to 0;

    //The margins can also be set using the printer stream member functions
    p.setLeftMargin(25);

    
    //We can put strings into the stream
    p << string("This is a very long string with lots of text  way past the eend of"
                "the riiiight margin to test the word break function.");
    p << string("This second string should start on a new line??");

    p << string("This short line\n has embeded newline char(s)");

    p<< string("This line contains\t embedded \t\t tab(s)");

    p << "Alonglinewithnospacestoseewhatwillhappenattheendofthelineabcdefghijklmnopqrstuvwxyz";

    //printing will start when we flush
    p << pst::flushp;

   
    //We can insert files into the stream
    ifstream f("testingx.cpp"); //use some available  text file
    p << f;   
    p << pst::flushp;

    return 0;
}