xref: /haiku/src/kits/interface/PrintJob.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
1 /*
2  * Copyright 2001-2006, Haiku.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  				I.R. Adema
7  				Stefano Ceccherini (burton666@libero.it)
8  				Michael Pfeiffer
9  */
10 
11 // TODO refactor (avoid code duplications, decrease method sizes)
12 
13 #include <Alert.h>
14 #include <Application.h>
15 #include <Button.h>
16 #include <Debug.h>
17 #include <Entry.h>
18 #include <File.h>
19 #include <FindDirectory.h>
20 #include <Messenger.h>
21 #include <NodeInfo.h>
22 #include <OS.h>
23 #include <Path.h>
24 #include <PrintJob.h>
25 #include <Roster.h>
26 #include <View.h>
27 
28 #include <pr_server.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 static const int kSemTimeOut = 50000;
35 
36 static const char *kPrintServerNotRespondingText = "Print Server is not responding.";
37 static const char *kNoPagesToPrintText = "No Pages to print!";
38 
39 // Summery of spool file format:
40 // See articel "How to Write a BeOS R5 Printer Driver" for description
41 // of spool file format: http://haiku-os.org/node/82
42 
43 // print_file_header header
44 // BMessage          job_settings
45 // _page_header_     page_header
46 // followed by number_of_pictures:
47 //   BPoint   where
48 //   BRect    bounds
49 //   BPicture picture
50 // remaining pages start at page_header.next_page of previous page_header
51 
52 struct _page_header_ {
53 	int32 number_of_pictures;
54 	off_t next_page;
55 	int32 reserved[10];
56 };
57 
58 
59 static status_t
60 GetPrinterServerMessenger(BMessenger& messenger)
61 {
62 	messenger = BMessenger(PSRV_SIGNATURE_TYPE);
63 	return messenger.IsValid() ? B_OK : B_ERROR;
64 }
65 
66 
67 static void
68 ShowError(const char *message)
69 {
70 	BAlert* alert = new BAlert("Error", message, "OK");
71 	alert->Go();
72 }
73 
74 
75 namespace BPrivate {
76 
77 	class Configuration {
78 		public:
79 			Configuration(uint32 what, BMessage *input);
80 			~Configuration();
81 
82 			status_t SendRequest(thread_func function);
83 
84 			BMessage* Request();
85 
86 			void SetResult(BMessage* result);
87 			BMessage* Result() const { return fResult; }
88 
89 		private:
90 			void RejectUserInput();
91 			void AllowUserInput();
92 			void DeleteSemaphore();
93 
94 			uint32 fWhat;
95 			BMessage *fInput;
96 			BMessage *fRequest;
97 			BMessage *fResult;
98 			sem_id fThreadCompleted;
99 			BAlert *fHiddenApplicationModalWindow;
100 	};
101 
102 
103 	Configuration::Configuration(uint32 what, BMessage *input)
104 		: fWhat(what),
105 		fInput(input),
106 		fRequest(NULL),
107 		fResult(NULL),
108 		fThreadCompleted(-1),
109 		fHiddenApplicationModalWindow(NULL)
110 	{
111 		RejectUserInput();
112 	}
113 
114 
115 	Configuration::~Configuration()
116 	{
117 		DeleteSemaphore();
118 			// in case SendRequest could not start the thread
119 		delete fRequest; fRequest = NULL;
120 		AllowUserInput();
121 	}
122 
123 
124 	void
125 	Configuration::RejectUserInput()
126 	{
127 		BAlert* alert = new BAlert("bogus", "app_modal_dialog", "OK");
128 		fHiddenApplicationModalWindow = alert;
129 		alert->DefaultButton()->SetEnabled(false);
130 		alert->SetDefaultButton(NULL);
131 		alert->MoveTo(-65000, -65000);
132 		alert->Go(NULL);
133 	}
134 
135 
136 	void
137 	Configuration::AllowUserInput()
138 	{
139 		fHiddenApplicationModalWindow->Lock();
140 		fHiddenApplicationModalWindow->Quit();
141 	}
142 
143 
144 	void
145 	Configuration::DeleteSemaphore()
146 	{
147 		if (fThreadCompleted >= B_OK) {
148 			sem_id id = fThreadCompleted;
149 			fThreadCompleted = -1;
150 			delete_sem(id);
151 		}
152 	}
153 
154 
155 	status_t
156 	Configuration::SendRequest(thread_func function)
157 	{
158 		fThreadCompleted = create_sem(0, "Configuration");
159 		if (fThreadCompleted < B_OK) {
160 			return B_ERROR;
161 		}
162 
163 		thread_id id = spawn_thread(function, "async_request", B_NORMAL_PRIORITY, this);
164 		if (id <= 0 || resume_thread(id) != B_OK) {
165 			return B_ERROR;
166 		}
167 
168 		// Code copied from BAlert::Go()
169 		BWindow* window = dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
170 			// Get the originating window, if it exists
171 
172 		// Heavily modified from TextEntryAlert code; the original didn't let the
173 		// blocked window ever draw.
174 		if (window != NULL) {
175 			status_t err;
176 			for (;;) {
177 				do {
178 					err = acquire_sem_etc(fThreadCompleted, 1, B_RELATIVE_TIMEOUT,
179 										  kSemTimeOut);
180 						// We've (probably) had our time slice taken away from us
181 				} while (err == B_INTERRUPTED);
182 
183 				if (err == B_BAD_SEM_ID) {
184 					// Semaphore was finally nuked in SetResult(BMessage *)
185 					break;
186 				}
187 				window->UpdateIfNeeded();
188 			}
189 		} else {
190 			// No window to update, so just hang out until we're done.
191 			while (acquire_sem(fThreadCompleted) == B_INTERRUPTED);
192 		}
193 
194 		status_t status;
195 		wait_for_thread(id, &status);
196 
197 		return Result() != NULL ? B_OK : B_ERROR;
198 	}
199 
200 
201 	BMessage *
202 	Configuration::Request()
203 	{
204 		if (fRequest != NULL)
205 			return fRequest;
206 
207 		if (fInput != NULL) {
208 			fRequest = new BMessage(*fInput);
209 			fRequest->what = fWhat;
210 		} else
211 			fRequest = new BMessage(fWhat);
212 		return fRequest;
213 	}
214 
215 
216 	void
217 	Configuration::SetResult(BMessage *result)
218 	{
219 		fResult = result;
220 		DeleteSemaphore();
221 			// terminate loop in thread spawned by SendRequest
222 	}
223 
224 } // BPrivate
225 
226 
227 BPrintJob::BPrintJob(const char *job_name)
228 	:
229 	fPrintJobName(NULL),
230 	fPageNumber(0),
231 	fSpoolFile(NULL),
232 	fError(B_ERROR),
233 	fSetupMessage(NULL),
234 	fDefaultSetupMessage(NULL),
235 	fCurrentPageHeader(NULL)
236 {
237 	memset(&fCurrentHeader, 0, sizeof(fCurrentHeader));
238 
239 	if (job_name != NULL) {
240 		fPrintJobName = strdup(job_name);
241 	}
242 
243 	fCurrentPageHeader = new _page_header_;
244 	if (fCurrentPageHeader != NULL) {
245 		memset(fCurrentPageHeader, 0, sizeof(*fCurrentPageHeader));
246 	}
247 }
248 
249 
250 BPrintJob::~BPrintJob()
251 {
252 	CancelJob();
253 
254 	if (fPrintJobName != NULL) {
255 		free(fPrintJobName);
256 		fPrintJobName = NULL;
257 	}
258 
259 	delete fDefaultSetupMessage;
260 	fDefaultSetupMessage = NULL;
261 
262 	delete fSetupMessage;
263 	fSetupMessage = NULL;
264 
265 	delete fCurrentPageHeader;
266 	fCurrentPageHeader = NULL;
267 }
268 
269 static
270 status_t ConfigPageThread(void *data)
271 {
272 	BPrivate::Configuration* configuration = static_cast<BPrivate::Configuration*>(data);
273 
274 	BMessenger printServer;
275 	if (GetPrinterServerMessenger(printServer) != B_OK) {
276 		ShowError(kPrintServerNotRespondingText);
277 		configuration->SetResult(NULL);
278 		return B_ERROR;
279 	}
280 
281 	BMessage *request = configuration->Request();
282 	if (request == NULL) {
283 		configuration->SetResult(NULL);
284 		return B_ERROR;
285 	}
286 
287 
288 	BMessage reply;
289 	if (printServer.SendMessage(request, &reply) != B_OK
290 		|| reply.what != 'okok') {
291 		configuration->SetResult(NULL);
292 		return B_ERROR;
293 	}
294 
295 	configuration->SetResult(new BMessage(reply));
296 	return B_OK;
297 }
298 
299 
300 status_t
301 BPrintJob::ConfigPage()
302 {
303 	BPrivate::Configuration configuration(PSRV_SHOW_PAGE_SETUP, fSetupMessage);
304 	status_t status = configuration.SendRequest(ConfigPageThread);
305 	if (status != B_OK)
306 		return status;
307 	delete fSetupMessage;
308 	fSetupMessage = configuration.Result();
309 	HandlePageSetup(fSetupMessage);
310 	return B_OK;
311 }
312 
313 
314 static status_t
315 ConfigJobThread(void *data)
316 {
317 	BPrivate::Configuration* configuration = static_cast<BPrivate::Configuration*>(data);
318 
319 	BMessenger printServer;
320 	if (GetPrinterServerMessenger(printServer) != B_OK) {
321 		ShowError(kPrintServerNotRespondingText);
322 		configuration->SetResult(NULL);
323 		return B_ERROR;
324 	}
325 
326 	BMessage *request = configuration->Request();
327 	if (request == NULL) {
328 		configuration->SetResult(NULL);
329 		return B_ERROR;
330 	}
331 
332 
333 	BMessage reply;
334 	if (printServer.SendMessage(request, &reply) != B_OK
335 		|| reply.what != 'okok') {
336 		configuration->SetResult(NULL);
337 		return B_ERROR;
338 	}
339 
340 	configuration->SetResult(new BMessage(reply));
341 	return B_OK;
342 }
343 
344 status_t
345 BPrintJob::ConfigJob()
346 {
347 	BPrivate::Configuration configuration(PSRV_SHOW_PRINT_SETUP, fSetupMessage);
348 	status_t status = configuration.SendRequest(ConfigJobThread);
349 	if (status != B_OK)
350 		return status;
351 	delete fSetupMessage;
352 	fSetupMessage = configuration.Result();
353 	HandlePrintSetup(fSetupMessage);
354 	return B_OK;
355 }
356 
357 
358 void
359 BPrintJob::BeginJob()
360 {
361 	if (fSpoolFile != NULL) {
362 		// can not start a new job until it has been commited or cancelled
363 		return;
364 	}
365 	if (fCurrentPageHeader == NULL) {
366 		return;
367 	}
368 
369 	if (fSetupMessage == NULL) {
370 		// TODO show alert, setup message is required
371 		return;
372 	}
373 
374 	// create spool file
375 	BPath path;
376 	status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
377 	if (status != B_OK)
378 		return;
379 
380 	char *printer = GetCurrentPrinterName();
381 	if (printer == NULL)
382 		return;
383 
384 	path.Append(printer);
385 	free(printer);
386 
387 	char mangledName[B_FILE_NAME_LENGTH];
388 	MangleName(mangledName);
389 
390 	path.Append(mangledName);
391 
392 	if (path.InitCheck() != B_OK)
393 		return;
394 
395 	// TODO fSpoolFileName should store the name only (not path which can be 1024 bytes long)
396 	strncpy(fSpoolFileName, path.Path(), sizeof(fSpoolFileName));
397 	fSpoolFile = new BFile(fSpoolFileName, B_READ_WRITE | B_CREATE_FILE);
398 
399 	if (fSpoolFile->InitCheck() != B_OK) {
400 		CancelJob();
401 		return;
402 	}
403 
404 	// add print_file_header
405 	// page_count is updated in CommitJob()
406 	fCurrentHeader.version = 1 << 16;
407 	fCurrentHeader.page_count = 0;
408 
409 	if (fSpoolFile->Write(&fCurrentHeader, sizeof(fCurrentHeader)) != sizeof(fCurrentHeader)) {
410 		CancelJob();
411 		return;
412 	}
413 
414 	// add printer settings message
415 	if (!fSetupMessage->HasString(PSRV_FIELD_CURRENT_PRINTER))
416 		fSetupMessage->AddString(PSRV_FIELD_CURRENT_PRINTER, printer);
417 	AddSetupSpec();
418 
419 	// prepare page header
420 	// number_of_pictures is updated in DrawView()
421 	// next_page is updated in SpoolPage()
422 	fCurrentPageHeaderOffset = fSpoolFile->Position();
423 	fCurrentPageHeader->number_of_pictures = 0;
424 
425 	// state variables
426 	fAbort = 0;
427 	fPageNumber = 0;
428 	fError = B_OK;
429 	return;
430 }
431 
432 
433 void
434 BPrintJob::CommitJob()
435 {
436 	if (fSpoolFile == NULL) {
437 		return;
438 	}
439 
440 	if (fPageNumber <= 0) {
441 		ShowError(kNoPagesToPrintText);
442 		CancelJob();
443 		return;
444 	}
445 
446 	if (fCurrentPageHeader->number_of_pictures > 0) {
447 		SpoolPage();
448 	}
449 
450 	// update spool file
451 	EndLastPage();
452 
453 	// set file attributes
454  	app_info appInfo;
455 	be_app->GetAppInfo(&appInfo);
456 	const char* printerName = "";
457 	fSetupMessage->FindString(PSRV_FIELD_CURRENT_PRINTER, &printerName);
458 
459 	BNodeInfo info(fSpoolFile);
460 	info.SetType(PSRV_SPOOL_FILETYPE);
461 
462 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PAGECOUNT, B_INT32_TYPE, 0, &fPageNumber, sizeof(int32));
463 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_DESCRIPTION, B_STRING_TYPE, 0, fPrintJobName, strlen(fPrintJobName) + 1);
464 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PRINTER, B_STRING_TYPE, 0, printerName, strlen(printerName) + 1);
465 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_STATUS, B_STRING_TYPE, 0, PSRV_JOB_STATUS_WAITING, strlen(PSRV_JOB_STATUS_WAITING) + 1);
466 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_MIMETYPE, B_STRING_TYPE, 0, appInfo.signature, strlen(appInfo.signature) + 1);
467 
468 	delete fSpoolFile;
469 	fSpoolFile = NULL;
470 	fError = B_ERROR;
471 
472 	// notify print server
473 	BMessenger printServer;
474 	if (GetPrinterServerMessenger(printServer) != B_OK) {
475 		return;
476 	}
477 
478 	BMessage request(PSRV_PRINT_SPOOLED_JOB);
479 	BMessage reply;
480 
481 	request.AddString("JobName", fPrintJobName);
482 	request.AddString("Spool File", fSpoolFileName);
483 	printServer.SendMessage(&request, &reply);
484 }
485 
486 void
487 BPrintJob::CancelJob()
488 {
489 	if (fSpoolFile == NULL) {
490 		return;
491 	}
492 
493 	fAbort = 1;
494 	BEntry(fSpoolFileName).Remove();
495 	delete fSpoolFile;
496 	fSpoolFile = NULL;
497 }
498 
499 
500 void
501 BPrintJob::SpoolPage()
502 {
503 	if (fSpoolFile == NULL) {
504 		return;
505 	}
506 
507 	// update page header
508 	fCurrentPageHeader->next_page = fSpoolFile->Position();
509 	fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
510 	fSpoolFile->Write(fCurrentPageHeader, sizeof(*fCurrentPageHeader));
511 	fSpoolFile->Seek(0, SEEK_END);
512 
513 	fCurrentPageHeader->number_of_pictures = 0;
514 }
515 
516 
517 bool
518 BPrintJob::CanContinue()
519 {
520 	// Check if our local error storage is still B_OK
521 	return fError == B_OK && !fAbort;
522 }
523 
524 
525 void
526 BPrintJob::DrawView(BView *view, BRect rect, BPoint where)
527 {
528 	if (fSpoolFile == NULL)
529 		return;
530 
531 	if (view == NULL)
532 		return;
533 
534 	if (view->LockLooper()) {
535 		BPicture picture;
536 		RecurseView(view, where, &picture, rect);
537 		AddPicture(&picture, &rect, where);
538 		view->UnlockLooper();
539 	}
540 }
541 
542 
543 BMessage *
544 BPrintJob::Settings()
545 {
546 	if (fSetupMessage == NULL) {
547 		return NULL;
548 	}
549 
550 	return new BMessage(*fSetupMessage);
551 }
552 
553 
554 void
555 BPrintJob::SetSettings(BMessage *message)
556 {
557 	if (message != NULL) {
558 		HandlePrintSetup(message);
559 	}
560 	delete fSetupMessage;
561 	fSetupMessage = message;
562 }
563 
564 
565 bool
566 BPrintJob::IsSettingsMessageValid(BMessage *message) const
567 {
568 	char *printerName = GetCurrentPrinterName();
569 	if (printerName == NULL) {
570 		return false;
571 	}
572 
573 	const char *name = NULL;
574 	// The passed message is valid if it contains the right printer name.
575 	bool valid = message != NULL
576 		&& message->FindString("printer_name", &name) == B_OK
577 		&& strcmp(printerName, name) == 0;
578 
579 	free(printerName);
580 
581 	return valid;
582 }
583 
584 // Either SetSettings() or ConfigPage() has to be called prior
585 // to any of the getters otherwise they return undefined values.
586 BRect
587 BPrintJob::PaperRect()
588 {
589 	return fPaperSize;
590 }
591 
592 
593 BRect
594 BPrintJob::PrintableRect()
595 {
596 	return fUsableSize;
597 }
598 
599 
600 void
601 BPrintJob::GetResolution(int32 *xdpi, int32 *ydpi)
602 {
603 	if (xdpi != NULL) {
604 		*xdpi = fXResolution;
605 	}
606 	if (ydpi != NULL) {
607 		*ydpi = fYResolution;
608 	}
609 }
610 
611 
612 int32
613 BPrintJob::FirstPage()
614 {
615     return fFirstPage;
616 }
617 
618 
619 int32
620 BPrintJob::LastPage()
621 {
622     return fLastPage;
623 }
624 
625 
626 int32
627 BPrintJob::PrinterType(void *) const
628 {
629 	BMessenger printServer;
630 	if (GetPrinterServerMessenger(printServer) != B_OK) {
631 		return B_COLOR_PRINTER; // default
632 	}
633 
634 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
635 	BMessage reply;
636 
637 	printServer.SendMessage(&message, &reply);
638 
639 	int32 type;
640 	if (reply.FindInt32("color", &type) != B_OK) {
641 		return B_COLOR_PRINTER; // default
642 	}
643 	return type;
644 }
645 
646 
647 #if 0
648 #pragma mark ----- PRIVATE -----
649 #endif
650 
651 
652 void
653 BPrintJob::RecurseView(BView *view, BPoint origin,
654                        BPicture *picture, BRect rect)
655 {
656 	ASSERT(picture != NULL);
657 
658 	view->AppendToPicture(picture);
659 	view->f_is_printing = true;
660 	view->Draw(rect);
661 	view->f_is_printing = false;
662 	view->EndPicture();
663 
664 	BView *child = view->ChildAt(0);
665 	while (child != NULL) {
666 		// TODO: origin and rect should probably
667 		// be converted for children views in some way
668 		RecurseView(child, origin, picture, rect);
669 		child = child->NextSibling();
670 	}
671 }
672 
673 
674 void
675 BPrintJob::MangleName(char *filename)
676 {
677 	char sysTime[10];
678 	snprintf(sysTime, sizeof(sysTime), "@%lld", system_time() / 1000);
679 	strncpy(filename, fPrintJobName, B_FILE_NAME_LENGTH - sizeof(sysTime));
680 	strcat(filename, sysTime);
681 }
682 
683 
684 void
685 BPrintJob::HandlePageSetup(BMessage *setup)
686 {
687 	setup->FindRect(PSRV_FIELD_PRINTABLE_RECT, &fUsableSize);
688 	setup->FindRect(PSRV_FIELD_PAPER_RECT, &fPaperSize);
689 
690 	// TODO verify data type (taken from libprint)
691 	int64 valueInt64;
692 	if (setup->FindInt64(PSRV_FIELD_XRES, &valueInt64) == B_OK) {
693 		fXResolution = (short)valueInt64;
694 	}
695 	if (setup->FindInt64(PSRV_FIELD_YRES, &valueInt64) == B_OK) {
696 		fYResolution = (short)valueInt64;
697 	}
698 }
699 
700 
701 bool
702 BPrintJob::HandlePrintSetup(BMessage *message)
703 {
704 	HandlePageSetup(message);
705 
706 	bool valid = true;
707 	if (message->FindInt32(PSRV_FIELD_FIRST_PAGE, &fFirstPage) != B_OK) {
708 		valid = false;
709 	}
710 	if (message->FindInt32(PSRV_FIELD_LAST_PAGE, &fLastPage) != B_OK) {
711 		valid = false;
712 	}
713 
714 	return valid;
715 }
716 
717 
718 void
719 BPrintJob::NewPage()
720 {
721 	// write page header
722 	fCurrentPageHeaderOffset = fSpoolFile->Position();
723 	fSpoolFile->Write(fCurrentPageHeader, sizeof(*fCurrentPageHeader));
724 
725 	if (fPageNumber == 1)
726 		fCurrentHeader.first_page = fCurrentPageHeaderOffset;
727 
728 	fPageNumber ++;
729 }
730 
731 
732 void
733 BPrintJob::EndLastPage()
734 {
735 	fSpoolFile->Seek(0, SEEK_SET);
736 	fCurrentHeader.page_count = fPageNumber;
737 	fSpoolFile->Write(&fCurrentHeader, sizeof(fCurrentHeader));}
738 
739 
740 void
741 BPrintJob::AddSetupSpec()
742 {
743 	fSetupMessage->Flatten(fSpoolFile);
744 }
745 
746 
747 void
748 BPrintJob::AddPicture(BPicture *picture, BRect *rect, BPoint where)
749 {
750 	ASSERT(picture != NULL);
751 	ASSERT(fSpoolFile != NULL);
752 	ASSERT(rect != NULL);
753 
754 	if (fCurrentPageHeader->number_of_pictures == 0) {
755 		NewPage();
756 	}
757 	fCurrentPageHeader->number_of_pictures ++;
758 
759 	fSpoolFile->Write(&where, sizeof(where));
760 	fSpoolFile->Write(rect, sizeof(*rect));
761 	picture->Flatten(fSpoolFile);
762 }
763 
764 
765 // Returns a copy of the current printer name
766 // or NULL if it ccould not be obtained.
767 // Caller is responsible to free the string using free().
768 char *
769 BPrintJob::GetCurrentPrinterName() const
770 {
771 	BMessenger printServer;
772 	if (GetPrinterServerMessenger(printServer)) {
773 		return NULL;
774 	}
775 
776 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
777 	BMessage reply;
778 
779 	const char *printerName = NULL;
780 
781 	if (printServer.SendMessage(&message, &reply) == B_OK) {
782 		reply.FindString("printer_name", &printerName);
783 	}
784 	if (printerName == NULL) {
785 		return NULL;
786 	}
787 	return strdup(printerName);
788 }
789 
790 
791 void
792 BPrintJob::LoadDefaultSettings()
793 {
794 	BMessenger printServer;
795 	if (GetPrinterServerMessenger(printServer) != B_OK) {
796 		return;
797 	}
798 
799 	BMessage message(PSRV_GET_DEFAULT_SETTINGS);
800 	BMessage *reply = new BMessage;
801 
802 	printServer.SendMessage(&message, reply);
803 
804 	HandlePrintSetup(reply);
805 
806 	delete fDefaultSetupMessage;
807 	fDefaultSetupMessage = reply;
808 }
809 
810 
811 void BPrintJob::_ReservedPrintJob1() {}
812 void BPrintJob::_ReservedPrintJob2() {}
813 void BPrintJob::_ReservedPrintJob3() {}
814 void BPrintJob::_ReservedPrintJob4() {}
815