釋出者:
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;
}