1/*
2 * Copyright 2001-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Philippe Houdoin
7 *		Simon Gauvin
8 *		Michael Pfeiffer
9 */
10
11/*!	Based on:
12	- gdevbjc.c from Aladdin GhostScript
13	- LMDriver.cpp from Ryan Lockhart Lexmark 5/7xxxx BeOS driver
14*/
15
16#include "PDFWriter.h"
17
18#include <ctype.h>
19#include <math.h>
20#include <stdio.h>
21#include <string.h>
22
23#include <Debug.h>
24#include <StorageKit.h>
25#include <TranslationKit.h>
26#include <support/UTF8.h>
27
28#include "Bezier.h"
29#include "LinePathBuilder.h"
30#include "DrawShape.h"
31#include "Log.h"
32#include "pdflib.h"
33#include "Bookmark.h"
34#include "XReferences.h"
35#include "Report.h"
36
37
38static const char* kEncodingDirectory = "PDF Writer";
39static const char* kSettingsDirectory = "PDF Writer";
40static const char* kBookmarksDirectory = "bookmarks";
41static const char* kCrossReferencesDirectory = "xrefs";
42
43
44PDFWriter::PDFWriter()
45	:
46	PrinterDriver(),
47	fTextLine(this)
48{
49	fFontSearchOrder[0] = japanese_encoding;
50	fFontSearchOrder[1] = chinese_cns1_encoding;
51	fFontSearchOrder[2]	= chinese_gb1_encoding;
52	fFontSearchOrder[3]	= korean_encoding;
53
54	fPage = 0;
55	fEmbedMaxFontSize = 250 * 1024;
56	fScreen = new BScreen();
57	fFonts = NULL;
58	fBookmark = new Bookmark(this);
59	fXRefs = new XRefDefs();
60	fXRefDests = NULL;
61}
62
63
64PDFWriter::~PDFWriter()
65{
66	delete fScreen;
67	delete fFonts;
68	delete fBookmark;
69	delete fXRefs;
70	delete fXRefDests;
71}
72
73
74//	#pragma mark Public methods
75
76
77status_t
78PDFWriter::PrintPage(int32	pageNumber, int32 pageCount)
79{
80	status_t status;
81	BRect r;
82	uint32 pictureCount;
83	BRect paperRect;
84	BRect printRect;
85	BRect *picRects;
86	BPoint *picPoints;
87	BRegion *picRegion;
88	BPicture **pictures;
89	uint32 i;
90	int32 orientation;
91
92	status = B_OK;
93
94	fPage = pageNumber;
95
96	if (pageNumber == 1) {
97		if (MakesPattern()) {
98			REPORT(kDebug, fPage, ">>>>> Collecting patterns...");
99		} else if (MakesPDF()) {
100			REPORT(kDebug, fPage, ">>>>> Generating PDF...");
101			fImageCache.NextPass();
102		}
103	}
104
105	paperRect = JobMsg()->FindRect("paper_rect");
106	printRect = JobMsg()->FindRect("printable_rect");
107	if (B_OK != JobMsg()->FindInt32("orientation", &orientation)) orientation = 0;
108	if (orientation == 1) {
109		printRect.Set(printRect.top, printRect.left,
110			printRect.bottom, printRect.right);
111	}
112
113	JobFile()->Read(&pictureCount, sizeof(uint32));
114
115	// TODO: error checking!
116	pictures = (BPicture **)malloc(pictureCount * sizeof(BPicture *));
117	picRects = (BRect *)malloc(pictureCount * sizeof(BRect));
118	picPoints = (BPoint *)malloc(pictureCount * sizeof(BPoint));
119	picRegion = new BRegion();
120
121	for (i = 0; i < pictureCount; i++) {
122		JobFile()->Seek(40 + sizeof(off_t), SEEK_CUR);
123		JobFile()->Read(&picPoints[i], sizeof(BPoint));
124		JobFile()->Read(&picRects[i], sizeof(BRect));
125		pictures[i] = new BPicture();
126		pictures[i]->Unflatten(JobFile());
127		picRegion->Include(picRects[i]);
128	}
129
130	r  = picRegion->Frame();
131	delete picRegion;
132
133	PDF_TRY(fPdf) {
134	BeginPage(paperRect, printRect);
135	for (i = 0; i < pictureCount; i++) {
136		SetOrigin(picPoints[i]);
137		PushInternalState();
138		Iterate(pictures[i]);
139		delete pictures[i];
140		PopInternalState();
141	}
142	EndPage();
143	} PDF_CATCH(fPdf) {
144		REPORT(kError, 0, PDF_get_errmsg(fPdf));
145	}
146
147	free(pictures);
148	free(picRects);
149	free(picPoints);
150
151	return status;
152}
153
154
155status_t
156PDFWriter::BeginJob()
157{
158	fLog = fopen("/tmp/pdf_writer.log", "w");
159
160	PDF_boot();
161
162	fPdf = PDF_new2(_ErrorHandler, NULL, NULL, NULL, this);
163		// set *this* as pdf cookie
164	if (fPdf == NULL)
165		return B_ERROR;
166
167	// load font embedding settings
168	fFonts = new Fonts();
169	fFonts->CollectFonts();
170	BMessage fonts;
171	if (B_OK == JobMsg()->FindMessage("fonts", &fonts))
172		fFonts->SetTo(&fonts);
173
174	// set font search order
175	int j = 0;
176	font_encoding enc;
177	bool active;
178
179	for (int i = 0; j < no_of_cjk_encodings
180			&& fFonts->GetCJKOrder(i, enc, active); i ++) {
181		if (active) fFontSearchOrder[j++] = enc;
182	}
183	for (; j < no_of_cjk_encodings; j ++) {
184		fFontSearchOrder[j] = invalid_encoding;
185	}
186
187	return InitWriter();
188}
189
190
191status_t
192PDFWriter::EndJob()
193{
194#ifdef DEBUG
195	Report* r = Report::Instance();
196	int32 n = r->CountItems();
197	fprintf(fLog, "Report:\n");
198	fprintf(fLog, "=======\n");
199	if (n == 0) {
200		fprintf(fLog, "<empty>\n");
201	}
202	for (int32 i = 0; i < n; i++) {
203		ReportRecord* rr = r->ItemAt(i);
204		switch (rr->Kind()) {
205			case kInfo:    fprintf(fLog, "Info"); break;
206			case kWarning: fprintf(fLog, "Warning"); break;
207			case kError:   fprintf(fLog, "Error"); break;
208			case kDebug:   fprintf(fLog, "Debug"); break;
209		}
210		fprintf(fLog, " %s", rr->Label());
211		if (rr->Page() > 0) fprintf(fLog, " (Page %d)", (int)rr->Page());
212		fprintf(fLog, ": %s\n", rr->Desc());
213	}
214#endif
215	fImageCache.Flush();
216
217	PDF_close(fPdf);
218	REPORT(kDebug, 0, ">>>> PDF_close");
219
220	PDF_delete(fPdf);
221	PDF_shutdown();
222
223	fclose(fLog);
224	return B_OK;
225}
226
227
228void
229PDFWriter::SetAttribute(const char* name, const char* value)
230{
231	if (!name || !value)
232		return;
233
234	BFile *file = dynamic_cast<BFile *>(Transport());
235	if (file != NULL)
236		file->WriteAttr(name, B_STRING_TYPE, 0, value, strlen(value) + 1);
237}
238
239
240status_t
241PDFWriter::InitWriter()
242{
243	char buffer[512];
244	BString s;
245
246	fState = NULL;
247	fStateDepth = 0;
248
249	// Set MIME type explicit as we *do* know it's a PDF document
250	BFile *file = dynamic_cast<BFile *>(Transport());
251	if (file)
252		BNodeInfo(file).SetType("application/pdf");
253
254	// pdflib scope: object
255/*
256	const char* license_key;
257	if (JobMsg()->FindString("pdflib_license_key", &license_key) == B_OK &&
258		license_key[0] != 0) {
259		REPORT(kDebug, 0, "license key found %s!", license_key);
260		PDF_set_parameter(fPdf, "license", license_key);
261	}
262*/
263
264	fPDFVersion = kPDF13;
265	const char *compatibility;
266	if (JobMsg()->FindString("pdf_compatibility", &compatibility) == B_OK) {
267		PDF_set_parameter(fPdf, "compatibility", compatibility);
268		if (strcmp(compatibility, "1.3") == 0) fPDFVersion = kPDF13;
269		else if (strcmp(compatibility, "1.4") == 0) fPDFVersion = kPDF14;
270		else if (strcmp(compatibility, "1.5") == 0) fPDFVersion = kPDF15;
271	}
272
273/*
274	// set user/master password
275	BString master_password, user_password;
276	if (JobMsg()->FindString("master_password", &master_password) == B_OK &&
277		JobMsg()->FindString("user_password", &user_password) == B_OK &&
278		master_password.Length() > 0 && user_password.Length() > 0) {
279		PDF_set_parameter(fPdf, "masterpassword", master_password.String());
280		PDF_set_parameter(fPdf, "userpassword", user_password.String());
281	}
282
283	// set permissions
284	BString permissions;
285	if (JobMsg()->FindString("permissions", &permissions) == B_OK &&
286		permissions.Length() > 0) {
287		PDF_set_parameter(fPdf, "permissions", permissions.String());
288	}
289*/
290
291	REPORT(kDebug, 0, ">>>> PDF_open_mem");
292	PDF_open_mem(fPdf, _WriteData);	// use callback to stream PDF document data to printer transport
293
294	// pdflib scope:
295
296	PDF_set_parameter(fPdf, "flush", "content");
297
298	// set document info
299	BMessage doc;
300	bool setTitle = true;
301	bool setCreator = true;
302	if (JobMsg()->FindMessage("doc_info", &doc) == B_OK) {
303#ifndef B_BEOS_VERSION_DANO
304		char *name;
305#else
306		const char *name;
307#endif
308		uint32 type;
309		int32 count;
310		for (int32 i = 0; doc.GetInfo(B_STRING_TYPE, i, &name, &type, &count)
311				!= B_BAD_INDEX; i++) {
312			if (type == B_STRING_TYPE) {
313				BString value;
314				if (doc.FindString(name, &value) == B_OK && value != "") {
315					SetAttribute(name, value.String());
316					BString s;
317					ToPDFUnicode(value.String(), s);
318					PDF_set_info(fPdf, name, s.String());
319				}
320			}
321		}
322		BString s;
323		if (doc.FindString("Title", &s) == B_OK && s != "")
324			setTitle = false;
325		if (doc.FindString("Creator", &s) == B_OK && s != "")
326			setCreator = false;
327	}
328
329	// find job title
330	if (setTitle && JobFile()->ReadAttr("_spool/Description", B_STRING_TYPE, 0,
331			buffer, sizeof(buffer))) {
332		SetAttribute("Title", buffer);
333	    ToPDFUnicode(buffer, s); PDF_set_info(fPdf, "Title", s.String());
334	}
335
336	// find job creator
337	if (setCreator && JobFile()->ReadAttr("_spool/MimeType", B_STRING_TYPE, 0,
338			buffer, sizeof(buffer))) {
339		SetAttribute("Creator", buffer);
340		ToPDFUnicode(buffer, s); PDF_set_info(fPdf, "Creator", s.String());
341	}
342
343	int32 compression;
344	if (JobMsg()->FindInt32("pdf_compression", &compression) == B_OK) {
345	    PDF_set_value(fPdf, "compress", compression);
346	}
347
348    // PDF_set_parameter(fPdf, "warning", "false");
349
350	PDF_set_parameter(fPdf, "fontwarning", "false");
351	// PDF_set_parameter(fPdf, "native-unicode", "true");
352
353	REPORT(kDebug, 0, "Start of declarations:");
354
355	DeclareEncodingFiles();
356	DeclareFonts();
357
358	REPORT(kDebug, fPage, "End of declarations.");
359
360	// Links
361	float width;
362	if (JobMsg()->FindFloat("link_border_width", &width) != B_OK) {
363		width = 1;
364	}
365	PDF_set_border_style(fPdf, "solid", width);
366
367	if (JobMsg()->FindBool("create_web_links", &fCreateWebLinks) != B_OK) {
368		fCreateWebLinks = false;
369	}
370
371	// Bookmarks
372	if (JobMsg()->FindBool("create_bookmarks", &fCreateBookmarks) != B_OK) {
373		fCreateBookmarks = false;
374	}
375
376	if (fCreateBookmarks) {
377		BString name;
378		if (JobMsg()->FindString("bookmark_definition_file", &name) == B_OK
379			&& LoadBookmarkDefinitions(name.String())) {
380		} else {
381			fCreateBookmarks = false;
382		}
383	}
384
385	// Cross References
386	if (JobMsg()->FindBool("create_xrefs", &fCreateXRefs) != B_OK) {
387		fCreateXRefs = false;
388	}
389
390	if (fCreateXRefs) {
391		BString name;
392		if (JobMsg()->FindString("xrefs_file", &name) == B_OK
393			&& LoadXRefsDefinitions(name.String())) {
394		} else {
395			REPORT(kError, 0, "Could not read xrefs file!");
396			fCreateXRefs = false;
397		}
398	}
399
400	return B_OK;
401}
402
403
404void
405PDFWriter::DeclareEncodingFiles()
406{
407	BPath prefix;
408	if (find_directory(B_SYSTEM_DATA_DIRECTORY, &prefix) != B_OK)
409		return;
410
411	DeclareEncodingFile(&prefix, "t1enc0", "t1enc0.enc");
412	DeclareEncodingFile(&prefix, "t1enc1", "t1enc1.enc");
413	DeclareEncodingFile(&prefix, "t1enc2", "t1enc2.enc");
414	DeclareEncodingFile(&prefix, "t1enc3", "t1enc3.enc");
415	DeclareEncodingFile(&prefix, "t1enc4", "t1enc4.enc");
416
417	DeclareEncodingFile(&prefix, "ttenc0", "ttenc0.cpg");
418	DeclareEncodingFile(&prefix, "ttenc1", "ttenc1.cpg");
419	DeclareEncodingFile(&prefix, "ttenc2", "ttenc2.cpg");
420	DeclareEncodingFile(&prefix, "ttenc3", "ttenc3.cpg");
421	DeclareEncodingFile(&prefix, "ttenc4", "ttenc4.cpg");
422}
423
424
425void
426PDFWriter::DeclareEncodingFile(BPath* prefix, const char* id, const char* name)
427{
428	BPath path(*prefix);
429	path.Append(kEncodingDirectory);
430	path.Append(name);
431
432	BString encodingDeclaration;
433	encodingDeclaration << id << "==" << path.Path();
434	PDF_set_parameter(fPdf, "Encoding", encodingDeclaration.String());
435}
436
437
438status_t
439PDFWriter::DeclareFonts()
440{
441	char buffer[1024];
442	const char *parameter_name;
443
444	for (int i = 0; i < fFonts->Length(); i++) {
445		FontFile* f = fFonts->At(i);
446		if (f->Type() == true_type_type) {
447			parameter_name = "FontOutline";
448		} else { // f->Type() == type1_type
449			if (strstr(f->Path(), ".afm"))
450				parameter_name = "FontAFM";
451			else if (strstr(f->Path(), ".pfm"))
452				parameter_name = "FontPFM";
453			else // should not reach here!
454				continue;
455		}
456#ifndef __POWERPC__
457		snprintf(buffer, sizeof(buffer), "%s==%s", f->Name(), f->Path());
458#else
459		sprintf(buffer, "%s==%s", f->Name(), f->Path());
460#endif
461		PDF_set_parameter(fPdf, parameter_name, buffer);
462	}
463	return B_OK;
464}
465
466
467bool
468PDFWriter::LoadBookmarkDefinitions(const char* name)
469{
470	BPath path;
471	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
472		return false;
473
474	path.Append(kSettingsDirectory);
475	path.Append(kBookmarksDirectory);
476
477	return fBookmark->Read(path.Path());
478}
479
480
481bool
482PDFWriter::LoadXRefsDefinitions(const char* name)
483{
484	BPath path;
485	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
486		return false;
487
488	path.Append(kSettingsDirectory);
489	path.Append(kCrossReferencesDirectory);
490
491	if (!fXRefs->Read(path.Path()))
492		return false;
493
494	fXRefDests = new XRefDests(fXRefs->Count());
495	return true;
496}
497
498
499status_t
500PDFWriter::BeginPage(BRect paperRect, BRect printRect)
501{
502	float width = paperRect.Width() < 10 ? a4_width : paperRect.Width();
503	float height = paperRect.Height() < 10 ? a4_height : paperRect.Height();
504
505	fMode = kDrawingMode;
506
507	ASSERT(fState == NULL);
508	fState = new State(height, printRect.left, printRect.top);
509
510	if (MakesPDF())
511		PDF_begin_page(fPdf, width, height);
512
513	REPORT(kDebug, fPage, ">>>> PDF_begin_page [%f, %f]", width, height);
514
515	if (MakesPDF())
516		PDF_initgraphics(fPdf);
517
518	fState->penX = 0;
519	fState->penY = 0;
520
521	// TODO: should we clip to the printRect here?
522
523	PushState(); // so that fState->prev != NULL
524
525	return B_OK;
526}
527
528
529status_t
530PDFWriter::EndPage()
531{
532	fTextLine.Flush();
533	if (fCreateBookmarks) fBookmark->CreateBookmarks();
534
535	while (fState->prev != NULL) PopState();
536
537	if (MakesPDF())
538		PDF_end_page(fPdf);
539	REPORT(kDebug, fPage, ">>>> PDF_end_page");
540
541	delete fState; fState = NULL;
542
543	return B_OK;
544}
545
546
547//	#pragma mark PDFlib callbacks
548
549
550size_t
551PDFWriter::WriteData(void *data, size_t	size)
552{
553	REPORT(kDebug, fPage, ">>>> WriteData %p, %ld", data, size);
554
555	return Transport()->Write(data, size);
556}
557
558
559void
560PDFWriter::ErrorHandler(int	type, const char *msg)
561{
562	REPORT(kError, fPage, "PDFlib %d: %s", type, msg);
563}
564
565
566//	#pragma mark Generic drawing support routines
567
568
569void
570PDFWriter::PushInternalState()
571{
572	REPORT(kDebug, fPage, "PushInternalState");
573	fState = new State(fState); fStateDepth ++;
574}
575
576
577bool
578PDFWriter::PopInternalState()
579{
580	REPORT(kDebug, fPage, "PopInternalState");
581	if (fStateDepth != 0) {
582		State* s = fState; fStateDepth --;
583		fState = fState->prev;
584		delete s;
585		return true;
586	} else {
587		REPORT(kDebug, fPage, "State stack underflow!");
588		return false;
589	}
590}
591
592
593PDFWriter::Transparency*
594PDFWriter::FindTransparency(uint8 alpha)
595{
596	const int n = fTransparencyCache.CountItems();
597	for (int i = 0; i < n; i ++) {
598		Transparency* t = fTransparencyCache.ItemAt(i);
599		if (t->Matches(alpha)) {
600			// return handle for existing gstate
601			return t;
602		}
603	}
604
605	// create new handle for gstate
606	char trans[256];
607	float a = (float)alpha/255.0;
608	sprintf(trans, "opacitystroke=%f opacityfill=%f", a, a);
609
610	volatile int handle = -1;
611	PDF_TRY(fPdf) {
612		handle = PDF_create_gstate(fPdf, trans);
613	} PDF_CATCH(fPdf) {
614		REPORT(kError, 0, PDF_get_errmsg(fPdf));
615	}
616	REPORT(kDebug, fPage, trans);
617
618	if (handle >= 0) {
619		// store in cache
620		Transparency* t = new Transparency(alpha, handle);
621		fTransparencyCache.AddItem(t);
622		return t;
623	}
624
625	return NULL;
626}
627
628
629void
630PDFWriter::BeginTransparency()
631{
632	if (!SupportsOpacity() || !MakesPDF() || !IsDrawing())
633		return;
634
635	REPORT(kDebug, fPage, ">>> BeginTransparency");
636	REPORT(kDebug, fPage, "current_color(%d, %d, %d, %d)",
637		fState->currentColor.red, fState->currentColor.green,
638		fState->currentColor.blue, fState->currentColor.alpha);
639	REPORT(kDebug, fPage, "drawing_mode %d alpha %d", (int)fState->drawingMode,
640		(int)fState->currentColor.alpha);
641
642	uint8 alpha = fState->currentColor.alpha;
643	if (fState->drawingMode == B_OP_ALPHA && alpha < 255) {
644		PDF_save(fPdf);
645		Transparency* t = FindTransparency(alpha);
646		if (t != NULL) {
647			PDF_TRY(fPdf) {
648				PDF_set_gstate(fPdf, t->Handle());
649			} PDF_CATCH(fPdf) {
650				REPORT(kError, 0, PDF_get_errmsg(fPdf));
651			}
652			fTransparencyStack.AddItem(t);
653			return;
654		}
655	}
656	// if transparency is not set then push NULL to transparency stack
657	fTransparencyStack.AddItem(NULL);
658}
659
660
661void
662PDFWriter::EndTransparency()
663{
664	if (!SupportsOpacity() || !MakesPDF() || !IsDrawing()) return;
665	REPORT(kDebug, fPage, "<<< EndTransparency");
666	int lastItem = fTransparencyStack.CountItems()-1;
667	Transparency* t = fTransparencyStack.RemoveItem(lastItem);
668
669	if (t != NULL) {
670		PDF_restore(fPdf);
671	}
672}
673
674
675void
676PDFWriter::SetColor(rgb_color color)
677{
678	if (!MakesPDF()) {
679		// create PDFlib gstate handles
680		if (SupportsOpacity() && fState->currentColor.alpha != color.alpha
681			&& color.alpha < 255) {
682			FindTransparency(color.alpha);
683		}
684	} else if (fState->currentColor.red != color.red ||
685		fState->currentColor.blue != color.blue ||
686		fState->currentColor.green != color.green ||
687		fState->currentColor.alpha != color.alpha) {
688		fState->currentColor = color;
689		float red   = color.red / 255.0;
690		float green = color.green / 255.0;
691		float blue  = color.blue / 255.0;
692		PDF_setcolor(fPdf, "both", "rgb", red, green, blue, 0.0);
693		REPORT(kDebug, fPage, "set_color(%f, %f, %f, %f)", red, green, blue,
694			color.alpha / 255.0);
695	}
696}
697
698
699int
700PDFWriter::FindPattern()
701{
702	// TODO: use hashtable instead of BList for fPatterns
703	const int n = fPatterns.CountItems();
704	for (int i = 0; i < n; i ++) {
705		Pattern* p = fPatterns.ItemAt(i);
706		if (p->Matches(fState->pattern0, fState->backgroundColor,
707				fState->foregroundColor))
708			return p->patternId;
709	}
710	return -1;
711}
712
713
714void
715PDFWriter::CreatePattern()
716{
717	REPORT(kDebug, fPage, "CreatePattern");
718#if 1
719	// use filled pathes for pattern
720	// pro: see con of bitmap pattern
721	// con: not rendered (correctly) in BePDF with small zoom factor
722	int pattern = PDF_begin_pattern(fPdf, 8, 8, 8, 8, 1);
723
724	if (pattern == -1) {
725		REPORT(kError, fPage, "CreatePattern could not create pattern");
726		return;
727	}
728
729	const int kPassForeground = 0;
730	const int kPassBackground = 1;
731	const int kNumPasses = 2;
732
733	for (int pass = 0; pass < kNumPasses; pass ++) {
734		float r, g, b;
735		bool is_transparent;
736
737		if (pass == kPassForeground) {
738			r =  fState->foregroundColor.red / 255.0;
739			g =  fState->foregroundColor.green / 255.0;
740			b =  fState->foregroundColor.blue / 255.0;
741			is_transparent = fState->foregroundColor.alpha < 128;
742		} else {
743			r =  fState->backgroundColor.red / 255.0;
744			g =  fState->backgroundColor.green / 255.0;
745			b =  fState->backgroundColor.blue / 255.0;
746			is_transparent = fState->backgroundColor.alpha < 128;
747		}
748
749		PDF_setcolor(fPdf, "fill", "rgb", r, g, b, 0);
750
751		if (is_transparent) continue;
752
753		uint8* data = (uint8*)fState->pattern0.data;
754		for (int8 y = 0; y <= 7; y ++, data ++) {
755			uint8  d = *data;
756			for (int8 x = 0; x <= 7; x ++, d >>= 1) {
757				if ((d & 1) == 1) { // foreground
758					if (pass != kPassForeground) continue;
759				} else { // background
760					if (pass != kPassBackground) continue;
761				}
762
763				// create rectangle for pixel
764				float x1 = x + 1, y1 = y + 1;
765				PDF_moveto(fPdf, x, y);
766				PDF_lineto(fPdf, x, y1);
767				PDF_lineto(fPdf, x1, y1);
768				PDF_lineto(fPdf, x1, y);
769				PDF_closepath(fPdf);
770				PDF_fill(fPdf);
771			}
772		}
773	}
774
775	PDF_end_pattern(fPdf);
776
777#else
778	// use bitmap for pattern
779	// pro: BePDF renders pattern also with small zoom factor
780	// con: BePDF omits mask during rendering
781	BBitmap bm(BRect(0, 0, 7, 7), B_RGBA32);
782
783	uint8* data = (uint8*)fState->pattern0.data;
784	rgb_color h = fState->foregroundColor;
785	rgb_color l = fState->backgroundColor;
786	if (h.alpha < 128) {
787		h.red = h.green = h.blue = 255;
788	}
789	if (l.alpha < 128) {
790		l.red = l.green = l.blue = 255;
791	}
792
793	uint8* row = (uint8*)bm.Bits();
794	for (int8 y = 0; y <= 7; y ++, data ++) {
795		uint8  d = *data;
796		uint8* b = row; row += bm.BytesPerRow();
797		for (int8 x = 0; x <= 7; x ++, d >>= 1, b += 4) {
798			if (d & 1 == 1) {
799				b[2] = h.red;   //fState->foregroundColor.red;
800				b[1] = h.green; //fState->foregroundColor.green;
801				b[0] = h.blue;  //fState->foregroundColor.blue;
802				b[3] = h.alpha; //fState->foregroundColor.alpha;
803			} else {
804				b[2] = l.red;   //fState->backgroundColor.red;
805				b[1] = l.green; //fState->backgroundColor.green;
806				b[0] = l.blue;  //fState->backgroundColor.blue;
807				b[3] = l.alpha; //fState->backgroundColor.alpha;
808			}
809		}
810	}
811
812	int mask, image;
813
814	if (!GetImages(bm.Bounds(), 8, 8, bm.BytesPerRow(), bm.ColorSpace(), 0,
815			bm.Bits(), &mask, &image)) {
816		return;
817	}
818
819	int pattern = PDF_begin_pattern(fPdf, 8, 8, 8, 8, 1);
820	if (pattern == -1) {
821		REPORT(kError, fPage, "CreatePattern could not create pattern");
822#if !USE_IMAGE_CACHE
823		PDF_close_image(fPdf, image);
824		if (mask != -1) PDF_close_image(fPdf, mask);
825#endif
826		return;
827	}
828	PDF_setcolor(fPdf, "both", "rgb", 0, 0, 1, 0);
829	PDF_place_image(fPdf, image, 0, 0, 1);
830	PDF_end_pattern(fPdf);
831#if !USE_IMAGE_CACHE
832	PDF_close_image(fPdf, image);
833	if (mask != -1) PDF_close_image(fPdf, mask);
834#endif
835#endif
836
837	Pattern* p = new Pattern(fState->pattern0, fState->backgroundColor,
838		fState->foregroundColor, pattern);
839	fPatterns.AddItem(p);
840}
841
842
843void
844PDFWriter::SetPattern()
845{
846	REPORT(kDebug, fPage, "SetPattern (bitmap version)");
847	if (MakesPattern()) {
848		if (FindPattern() == -1) CreatePattern();
849	} else {
850		int pattern = FindPattern();
851		if (pattern != -1) {
852			PDF_setcolor(fPdf, "both", "pattern", pattern, 0, 0, 0);
853		} else {
854			// TODO: fall back to another method
855			REPORT(kError, fPage, "pattern missing!");
856		}
857	}
858}
859
860
861void
862PDFWriter::StrokeOrClip()
863{
864	if (IsDrawing()) {
865		PDF_stroke(fPdf);
866	} else {
867		REPORT(kError, fPage, "Clipping not implemented for this primitive!!!");
868		PDF_closepath(fPdf);
869	}
870}
871
872
873void
874PDFWriter::FillOrClip()
875{
876	if (IsDrawing()) {
877		PDF_fill(fPdf);
878	} else {
879		PDF_closepath(fPdf);
880	}
881}
882
883
884void
885PDFWriter::Paint(bool stroke)
886{
887	if (stroke) {
888		StrokeOrClip();
889	} else {
890		FillOrClip();
891	}
892}
893
894
895void
896PDFWriter::SetColor()
897{
898	if (IsSame(fState->pattern0, B_SOLID_HIGH)) {
899		SetColor(fState->foregroundColor);
900	} else if (IsSame(fState->pattern0, B_SOLID_LOW)) {
901		SetColor(fState->backgroundColor);
902	} else {
903		SetPattern();
904	}
905}
906
907
908//	#pragma mark Image drawing support routines
909
910
911int32
912PDFWriter::BytesPerPixel(int32 pixelFormat)
913{
914	switch (pixelFormat) {
915		case B_RGB32:      // fall through
916		case B_RGB32_BIG:  // fall through
917		case B_RGBA32:     // fall through
918		case B_RGBA32_BIG:
919			return 4;
920
921		case B_RGB24_BIG:  // fall through
922		case B_RGB24:
923			return 3;
924
925		case B_RGB16:      // fall through
926		case B_RGB16_BIG:  // fall through
927		case B_RGB15:      // fall through
928		case B_RGB15_BIG:  // fall through
929		case B_RGBA15:     // fall through
930		case B_RGBA15_BIG:
931			return 2;
932
933		case B_GRAY8:      // fall through
934		case B_CMAP8:
935			return 1;
936		case B_GRAY1:
937			return 0;
938		default: return -1;
939	}
940}
941
942
943bool
944PDFWriter::HasAlphaChannel(int32 pixelFormat)
945{
946	switch (pixelFormat) {
947		case B_RGB32:      // fall through
948		case B_RGB32_BIG:  // fall through
949		case B_RGBA32:     // fall through
950		case B_RGBA32_BIG: // fall through
951//		case B_RGB24:      // fall through
952//		case B_RGB24_BIG:  // fall through
953//		case B_RGB16:      // fall through
954//		case B_RGB16_BIG:  // fall through
955		case B_RGB15:      // fall through
956		case B_RGB15_BIG:  // fall through
957		case B_RGBA15:     // fall through
958		case B_RGBA15_BIG: // fall through
959		case B_CMAP8:
960			return true;
961		default:
962			return false;
963	}
964}
965
966
967bool
968PDFWriter::NeedsBPC1Mask(int32 pixelFormat)
969{
970	switch (pixelFormat) {
971//		case B_RGB32:      // fall through
972//		case B_RGB32_BIG:  // fall through
973		case B_RGB15:      // fall through
974		case B_RGB15_BIG:  // fall through
975		case B_RGBA15:     // fall through
976		case B_RGBA15_BIG: // fall through
977		case B_CMAP8:
978			return true;
979		default:
980			return false;
981	};
982}
983
984
985bool
986PDFWriter::IsTransparentRGB32(uint8* in)
987{
988	return *((uint32*)in) == B_TRANSPARENT_MAGIC_RGBA32;
989}
990
991
992bool
993PDFWriter::IsTransparentRGB32_BIG(uint8* in)
994{
995	return *(uint32*)in == B_TRANSPARENT_MAGIC_RGBA32_BIG;
996}
997
998
999bool
1000PDFWriter::IsTransparentRGBA32(uint8* in)
1001{
1002	return in[3] < 128 || IsTransparentRGB32(in);
1003}
1004
1005
1006bool
1007PDFWriter::IsTransparentRGBA32_BIG(uint8* in)
1008{
1009	return in[0] < 127 || IsTransparentRGB32_BIG(in);
1010}
1011
1012
1013bool
1014PDFWriter::IsTransparentRGB15(uint8* in)
1015{
1016	return *((uint16*)in) == B_TRANSPARENT_MAGIC_RGBA15;
1017}
1018
1019
1020bool
1021PDFWriter::IsTransparentRGB15_BIG(uint8* in)
1022{
1023	// 01234567 01234567
1024	// 00123434 01201234
1025	// -RRRRRGG GGGBBBBB
1026	return *(uint16*)in == B_TRANSPARENT_MAGIC_RGBA15_BIG;
1027}
1028
1029
1030bool
1031PDFWriter::IsTransparentRGBA15(uint8* in)
1032{
1033	// 01234567 01234567
1034	// 01201234 00123434
1035	// GGGBBBBB ARRRRRGG
1036	return (in[1] & 1) == 0 || IsTransparentRGB15(in);
1037}
1038
1039
1040bool
1041PDFWriter::IsTransparentRGBA15_BIG(uint8* in)
1042{
1043	// 01234567 01234567
1044	// 00123434 01201234
1045	// ARRRRRGG GGGBBBBB
1046	return (in[0] & 1) == 0 || IsTransparentRGB15_BIG(in);
1047}
1048
1049
1050bool
1051PDFWriter::IsTransparentCMAP8(uint8* in)
1052{
1053	return *in == B_TRANSPARENT_MAGIC_CMAP8;
1054}
1055
1056
1057uint8 *
1058PDFWriter::CreateMask(BRect src, int32 bytesPerRow, int32 pixelFormat,
1059	int32 flags, void *data)
1060{
1061	uint8	*in;
1062	uint8   *inRow;
1063	int32	x, y;
1064
1065	uint8	*mask;
1066	uint8	*maskRow;
1067	uint8	*out;
1068	int32	maskWidth;
1069	uint8	shift;
1070	bool	alpha;
1071	int32   bpp;
1072
1073	bpp = BytesPerPixel(pixelFormat);
1074	if (bpp < 0)
1075		return NULL;
1076
1077	int32	width = src.IntegerWidth() + 1;
1078	int32	height = src.IntegerHeight() + 1;
1079
1080	// Image Mask
1081	inRow = (uint8 *) data;
1082	inRow += bytesPerRow * (int) src.top + bpp * (int) src.left;
1083
1084	maskWidth = (width+7)/8;
1085	maskRow = mask = new uint8[maskWidth * height];
1086	memset(mask, 0, maskWidth*height);
1087	alpha = false;
1088
1089	for (y = height; y > 0; y--) {
1090		in = inRow;
1091		out = maskRow;
1092		shift = 7;
1093
1094		bool a = false;
1095
1096		for (x = width; x > 0; x-- ) {
1097			// For each pixel
1098			switch (pixelFormat) {
1099				case B_RGB32:      a = IsTransparentRGB32(in); break;
1100				case B_RGB32_BIG:  a = IsTransparentRGB32_BIG(in); break;
1101				case B_RGBA32:     a = IsTransparentRGBA32(in); break;
1102				case B_RGBA32_BIG: a = IsTransparentRGBA32_BIG(in); break;
1103				//case B_RGB24:      a = IsTransparentRGB24(in); break;
1104				//case B_RGB24_BIG:  a = IsTransparentRGB24_BIG(in); break;
1105				//case B_RGB16:      a = IsTransparentRGB16(in); break;
1106				//case B_RGB16_BIG:  a = IsTransparentRGB16_BIG(in); break;
1107				case B_RGB15:      a = IsTransparentRGB15(in); break;
1108				case B_RGB15_BIG:  a = IsTransparentRGB15_BIG(in); break;
1109				case B_RGBA15:     a = IsTransparentRGBA15(in); break;
1110				case B_RGBA15_BIG: a = IsTransparentRGBA15_BIG(in); break;
1111				case B_CMAP8:      a = IsTransparentCMAP8(in); break;
1112				//case B_GRAY8:      a = IsTransparentGRAY8(in); break;
1113				//case B_GRAY1:      a = false; break;
1114				default:
1115					a = false; // should not reach here
1116					REPORT(kDebug, fPage,
1117						"CreateMask: non transparentable pixelFormat");
1118			}
1119
1120			if (a) {
1121				out[0] |= (1 << shift);
1122				alpha = true;
1123			}
1124			// next pixel
1125			if (shift == 0) out ++;
1126			shift = (shift + 7) & 0x07;
1127			in += bpp;
1128		}
1129
1130		// next row
1131		inRow += bytesPerRow;
1132		maskRow += maskWidth;
1133	}
1134
1135	if (!alpha) {
1136		delete []mask;
1137		mask = NULL;
1138	}
1139	return mask;
1140}
1141
1142
1143uint8
1144PDFWriter::AlphaFromRGBA32(uint8* in)
1145{
1146	return in[2];
1147}
1148
1149
1150uint8
1151PDFWriter::AlphaFromRGBA32_BIG(uint8* in)
1152{
1153	return in[0];
1154}
1155
1156
1157uint8 *
1158PDFWriter::CreateSoftMask(BRect src, int32 bytesPerRow, int32 pixelFormat,
1159	int32 flags, void *data)
1160{
1161#if 1
1162	// TODO: CreateSoftMask???
1163	return NULL;
1164#else
1165	static bool errorReported = false;
1166
1167	uint8	*in;
1168	uint8   *inRow;
1169	int32	x, y;
1170
1171	uint8	*mask;
1172	uint8	*maskRow;
1173	uint8	*out;
1174	bool	alpha;
1175	int32   bpp;
1176
1177	bpp = BytesPerPixel(pixelFormat);
1178	if (bpp < 0)
1179		return NULL;
1180
1181	int32 width = src.IntegerWidth() + 1;
1182	int32 height = src.IntegerHeight() + 1;
1183
1184	// Image Mask
1185	inRow = (uint8 *) data;
1186	inRow += bytesPerRow * (int) src.top + bpp * (int) src.left;
1187
1188	// soft mask with 8 bits per component
1189	maskRow = mask = new uint8[width * height];
1190	memset(mask, 0, width * height);
1191	alpha = false;
1192
1193	for (y = height; y > 0; y--) {
1194		in = inRow;
1195		out = maskRow;
1196
1197		uint8 a;
1198
1199		for (x = width; x > 0; x-- ) {
1200			// For each pixel
1201			switch (pixelFormat) {
1202				case B_RGB32:      // fall through
1203				case B_RGBA32:
1204					a = AlphaFromRGBA32(in);
1205					break;
1206
1207				case B_RGB32_BIG:  // fall through
1208				case B_RGBA32_BIG:
1209					a = AlphaFromRGBA32_BIG(in);
1210					break;
1211
1212				default:
1213					a = 255; // should not reach here
1214					if (!errorReported) {
1215						REPORT(kDebug, fPage,
1216							"CreateSoftMask: non transparentable pixelFormat");
1217						errorReported = true;
1218					}
1219			}
1220
1221			*out = a;
1222			if (a != 255) {
1223				alpha = true;
1224			}
1225			// next pixel
1226			out ++;
1227			in += bpp;
1228		}
1229
1230		// next row
1231		inRow += bytesPerRow;
1232		maskRow += width;
1233	}
1234
1235	if (!alpha) {
1236		delete []mask;
1237		mask = NULL;
1238	}
1239	return mask;
1240#endif
1241}
1242
1243
1244void
1245PDFWriter::ConvertFromRGB32(uint8* in, uint8 *out)
1246{
1247	*((rgb_color*)out) = *((rgb_color*)in);
1248}
1249
1250
1251void
1252PDFWriter::ConvertFromRGBA32(uint8* in, uint8 *out)
1253{
1254	*((rgb_color*)out) = *((rgb_color*)in);
1255}
1256
1257
1258void
1259PDFWriter::ConvertFromRGB24(uint8* in, uint8 *out)
1260{
1261	out[0] = in[0];
1262	out[1] = in[1];
1263	out[2] = in[2];
1264	out[3] = 255;
1265}
1266
1267
1268void
1269PDFWriter::ConvertFromRGB16(uint8* in, uint8 *out)
1270{
1271	// 01234567 01234567
1272	// 01201234 01234345
1273	// GGGBBBBB RRRRRGGG
1274	out[0] = in[0] & 0xf8; // blue
1275	out[1] = ((in[0] & 7) << 2) | (in[1] & 0xe0); // green
1276	out[2] = in[1] << 3; // red
1277	out[3] = 255;
1278}
1279
1280
1281void
1282PDFWriter::ConvertFromRGB15(uint8* in, uint8 *out)
1283{
1284	// 01234567 01234567
1285	// 01201234 00123434
1286	// GGGBBBBB -RRRRRGG
1287	out[0] = in[0] & 0xf8; // blue
1288	out[1] = ((in[0] & 7) << 3) | (in[1] & 0xc0); // green
1289	out[2] = (in[1] & ~1) << 2; // red
1290	out[3] = 255;
1291}
1292
1293
1294void
1295PDFWriter::ConvertFromRGBA15(uint8* in, uint8 *out)
1296{
1297	// 01234567 01234567
1298	// 01201234 00123434
1299	// GGGBBBBB ARRRRRGG
1300	out[0] = in[0] & 0xf8; // blue
1301	out[1] = ((in[0] & 7) << 3) | (in[1] & 0xc0); // green
1302	out[2] = (in[1] & ~1) << 2; // red
1303	out[3] = in[1] << 7;
1304}
1305
1306
1307void
1308PDFWriter::ConvertFromCMAP8(uint8* in, uint8 *out)
1309{
1310	rgb_color c = fScreen->ColorForIndex(in[0]);
1311	out[0] = c.blue;
1312	out[1] = c.green;
1313	out[2] = c.red;
1314	out[3] = c.alpha;
1315}
1316
1317
1318void
1319PDFWriter::ConvertFromGRAY8(uint8* in, uint8 *out)
1320{
1321	out[0] = in[0];
1322	out[1] = in[0];
1323	out[2] = in[0];
1324	out[3] = 255;
1325}
1326
1327
1328void
1329PDFWriter::ConvertFromGRAY1(uint8* in, uint8 *out, int8 bit)
1330{
1331	uint8 gray = (in[0] & (1 << bit)) ? 255 : 0;
1332	out[0] = gray;
1333	out[1] = gray;
1334	out[2] = gray;
1335	out[3] = 255;
1336}
1337
1338
1339void
1340PDFWriter::ConvertFromRGB32_BIG(uint8* in, uint8 *out)
1341{
1342	out[0] = in[3];
1343	out[1] = in[2];
1344	out[2] = in[1];
1345	out[3] = 255;
1346}
1347
1348
1349void
1350PDFWriter::ConvertFromRGBA32_BIG(uint8* in, uint8 *out)
1351{
1352	out[0] = in[3];
1353	out[1] = in[2];
1354	out[2] = in[1];
1355	out[3] = in[0];
1356}
1357
1358
1359void
1360PDFWriter::ConvertFromRGB24_BIG(uint8* in, uint8 *out)
1361{
1362	out[0] = in[2];
1363	out[1] = in[1];
1364	out[2] = in[0];
1365	out[3] = 255;
1366}
1367
1368
1369void
1370PDFWriter::ConvertFromRGB16_BIG(uint8* in, uint8 *out)
1371{
1372	// 01234567 01234567
1373	// 01234345 01201234
1374	// RRRRRGGG GGGBBBBB
1375	out[0] = in[2] & 0xf8; // blue
1376	out[1] = ((in[1] & 7) << 2) | (in[0] & 0xe0); // green
1377	out[2] = in[0] << 3; // red
1378	out[3] = 255;
1379}
1380
1381
1382void
1383PDFWriter::ConvertFromRGB15_BIG(uint8* in, uint8 *out)
1384{
1385	// 01234567 01234567
1386	// 00123434 01201234
1387	// -RRRRRGG GGGBBBBB
1388	out[0] = in[1] & 0xf8; // blue
1389	out[1] = ((in[1] & 7) << 3) | (in[0] & 0xc0); // green
1390	out[2] = (in[0] & ~1) << 2; // red
1391	out[3] = 255;
1392}
1393
1394
1395void
1396PDFWriter::ConvertFromRGBA15_BIG(uint8* in, uint8 *out)
1397{
1398	// 01234567 01234567
1399	// 00123434 01201234
1400	// ARRRRRGG GGGBBBBB
1401	out[0] = in[1] & 0xf8; // blue
1402	out[1] = ((in[1] & 7) << 3) | (in[0] & 0xc0); // green
1403	out[2] = (in[0] & ~1) << 2; // red
1404	out[3] = in[0] << 7;
1405}
1406
1407
1408//! Convert and clip bits to colorspace B_RGBA32
1409BBitmap *
1410PDFWriter::ConvertBitmap(BRect src, int32 bytesPerRow, int32 pixelFormat,
1411	int32 flags, void *data)
1412{
1413	uint8	*in;
1414	uint8   *inLeft;
1415	uint8	*out;
1416	uint8   *outLeft;
1417	int32	x, y;
1418	int8    bit;
1419	int32   bpp = 4;
1420
1421	bpp = BytesPerPixel(pixelFormat);
1422	if (bpp < 0)
1423		return NULL;
1424
1425	int32 width  = src.IntegerWidth();
1426	int32 height = src.IntegerHeight();
1427	BBitmap *bm = new BBitmap(BRect(0, 0, width, height), B_RGB32);
1428	if (!bm->IsValid()) {
1429		delete bm;
1430		REPORT(kError, fPage, "BBitmap constructor failed");
1431		return NULL;
1432	}
1433
1434	inLeft  = (uint8 *)data;
1435	inLeft += bytesPerRow * (int)src.top + bpp * (int)src.left;
1436	outLeft	= (uint8*)bm->Bits();
1437
1438	for (y = height; y >= 0; y--) {
1439		in = inLeft;
1440		out = outLeft;
1441
1442		for (x = 0; x <= width; x++) {
1443			// For each pixel
1444			switch (pixelFormat) {
1445				case B_RGB32:      ConvertFromRGB32(in, out); break;
1446				case B_RGBA32:     ConvertFromRGBA32(in, out); break;
1447				case B_RGB24:      ConvertFromRGB24(in, out); break;
1448				case B_RGB16:      ConvertFromRGB16(in, out); break;
1449				case B_RGB15:      ConvertFromRGB15(in, out); break;
1450				case B_RGBA15:     ConvertFromRGBA15(in, out); break;
1451				case B_CMAP8:      ConvertFromCMAP8(in, out); break;
1452				case B_GRAY8:      ConvertFromGRAY8(in, out); break;
1453				case B_GRAY1:
1454					bit = x & 7;
1455					ConvertFromGRAY1(in, out, bit);
1456					if (bit == 7) in ++;
1457					break;
1458				case B_RGB32_BIG:  ConvertFromRGB32_BIG(in, out); break;
1459				case B_RGBA32_BIG: ConvertFromRGBA32_BIG(in, out); break;
1460				case B_RGB24_BIG:  ConvertFromRGB24_BIG(in, out); break;
1461				case B_RGB16_BIG:  ConvertFromRGB16_BIG(in, out); break;
1462				case B_RGB15_BIG:  ConvertFromRGB15_BIG(in, out); break;
1463				case B_RGBA15_BIG: ConvertFromRGBA15_BIG(in, out); break;
1464				default:; // should not reach here
1465			}
1466			in += bpp;	// next pixel
1467			out += 4;
1468		}
1469
1470		// next row
1471		inLeft += bytesPerRow;
1472		outLeft += bm->BytesPerRow();
1473	}
1474
1475	return bm;
1476}
1477
1478
1479bool
1480PDFWriter::StoreTranslatorBitmap(BBitmap *bitmap, const char *filename,
1481	uint32 type)
1482{
1483	BTranslatorRoster *roster = BTranslatorRoster::Default();
1484	if (!roster) {
1485		REPORT(kDebug, fPage, "TranslatorRoster is NULL!");
1486		return false;
1487	}
1488	BBitmapStream stream(bitmap); // init with contents of bitmap
1489/*
1490	translator_info info, *i = NULL;
1491	if (quality != -1.0 && roster->Identify(&stream, NULL, &info) == B_OK) {
1492		#ifdef DEBUG
1493		LOG((fLog, ">>> translator_info:\n"));
1494		LOG((fLog, "   type = %4.4s\n", (char*)&info.type));
1495		LOG((fLog, "   id = %d\n", info.translator));
1496		LOG((fLog, "   group = %4.4s\n", (char*)&info.group));
1497		LOG((fLog, "   quality = %f\n", info.quality));
1498		LOG((fLog, "   capability = %f\n", info.type));
1499		LOG((fLog, "   name = %s\n", info.name));
1500		LOG((fLog, "   MIME = %s\n", info.MIME));
1501		#endif
1502
1503		int32 numInfo;
1504		translator_info *tInfo = NULL;
1505		if (roster->GetTranslators(&stream, NULL, &tInfo, &numInfo,
1506			info.type, NULL, type) == B_OK) {
1507
1508//			#ifdef DEBUG
1509			for (int j = 0; j < numInfo; j++) {
1510				LOG((fLog, ">>> translator_info [%d]:\n", j));
1511				LOG((fLog, "   type = %4.4s\n", (char*)&tInfo[j].type));
1512				LOG((fLog, "   id = %d\n", tInfo[j].translator));
1513				LOG((fLog, "   group = %4.4s\n", (char*)&tInfo[j].group));
1514				LOG((fLog, "   quality = %f\n", tInfo[j].quality));
1515				LOG((fLog, "   capability = %f\n", tInfo[j].type));
1516				LOG((fLog, "   name = %s\n", tInfo[j].name));
1517				LOG((fLog, "   MIME = %s\n", tInfo[j].MIME));
1518			}
1519//			#endif
1520
1521			BMessage m;
1522			status_t s = roster->GetConfigurationMessage(tInfo[0].translator, &m);
1523			if (s == B_OK) {
1524				fMessagePrinter.Print(&m);
1525			} else {
1526				LOG((fLog, "ERROR: could not get configuration message %d\n", s));
1527			}
1528
1529			info = tInfo[0];
1530			info.quality = quality;
1531			delete []tInfo;
1532
1533			i = &info;
1534		}
1535	}
1536*/
1537	BFile file(filename, B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE);
1538	bool res = roster->Translate(&stream, NULL /*i*/, NULL, &file, type) == B_OK;
1539	BBitmap *bm = NULL;
1540	// detach bitmap so BBitmapStream destructor does not delete our bitmap!
1541	stream.DetachBitmap(&bm);
1542	ASSERT(bm == bitmap);
1543	return res;
1544}
1545
1546
1547bool
1548PDFWriter::GetImages(BRect src, int32 /*width*/, int32 /*height*/,
1549	int32 bytesPerRow, int32 pixelFormat, int32 flags, void *data, int* maskId,
1550	int* image)
1551{
1552	uint8 *mask = NULL;
1553	*maskId = -1;
1554
1555	int32 width = src.IntegerWidth() + 1;
1556	int32 height = src.IntegerHeight() + 1;
1557
1558	int length = 0;
1559	int bpc = 0;
1560
1561	if (HasAlphaChannel(pixelFormat)) {
1562		if (NeedsBPC1Mask(pixelFormat) || !SupportsSoftMask()) {
1563			int32 w = (width+7)/8;
1564			length = w * height;
1565			bpc = 1;
1566			mask = CreateMask(src, bytesPerRow, pixelFormat, flags, data);
1567			REPORT(kDebug, fPage, "Mask created mask = %p", mask);
1568		} else {
1569			length = width * height;
1570			bpc = 8;
1571			mask = CreateSoftMask(src, bytesPerRow, pixelFormat, flags, data);
1572			REPORT(kDebug, fPage, "SoftMask created mask = %p", mask);
1573		}
1574	}
1575
1576	if (mask) {
1577// PDFlib deprecated:
1578//		*maskId = PDF_open_image(fPdf, "raw", "memory", (const char *) mask, length, width, height, 1, bpc, "mask");
1579#if USE_IMAGE_CACHE
1580		*maskId = fImageCache.GetMask(fPdf, (char*)mask, length, width, height, bpc);
1581#else
1582		BString options;
1583		PDF_create_pvf(fPdf, "mask", 0, mask, length, NULL);
1584		options << "width " << width << " height " << height << " components 1 bpc " << bpc;
1585		*maskId = PDF_load_image(fPdf, "raw", "mask", 0, options.String());
1586		PDF_delete_pvf(fPdf, "mask", 0);
1587#endif
1588		delete []mask;
1589	}
1590
1591	BBitmap * bm = ConvertBitmap(src, bytesPerRow, pixelFormat, flags, data);
1592	if (!bm) {
1593		REPORT(kError, fPage, "ConvertBitmap failed!");
1594#if !USE_IMAGE_CACHE
1595		if (*maskId != -1) PDF_close_image(fPdf, *maskId);
1596#endif
1597		return false;
1598	}
1599
1600#if USE_IMAGE_CACHE
1601	*image = fImageCache.GetImage(fPdf, bm, *maskId);
1602	delete bm;
1603#else
1604	char *pdfLibFormat   = "png";
1605	char *bitmapFileName = "/tmp/pdfwriter.png";
1606	const uint32 beosFormat    = B_PNG_FORMAT;
1607
1608	if (!StoreTranslatorBitmap(bm, bitmapFileName, beosFormat)) {
1609		delete bm;
1610		REPORT(kError, fPage, "StoreTranslatorBitmap failed");
1611#if !USE_IMAGE_CACHE
1612		if (*maskId != -1) PDF_close_image(fPdf, *maskId);
1613#endif
1614		return false;
1615	}
1616	delete bm;
1617
1618	*image = PDF_open_image_file(fPdf, pdfLibFormat, bitmapFileName,
1619		*maskId == -1 ? "" : "masked", *maskId == -1 ? 0 : *maskId);
1620#endif
1621
1622	return *image >= 0;
1623}
1624
1625
1626//	#pragma mark BPicture playback handlers
1627
1628
1629void
1630PDFWriter::Op(int number)
1631{
1632	REPORT(kError, fPage, "Unhandled operand %d", number);
1633}
1634
1635
1636void
1637PDFWriter::MovePenBy(BPoint delta)
1638{
1639	REPORT(kDebug, fPage, "MovePenBy delta=[%f, %f]", delta.x, delta.y);
1640	fState->penX += delta.x;
1641	fState->penY += delta.y;
1642}
1643
1644
1645void
1646PDFWriter::StrokeLine(BPoint start,	BPoint end)
1647{
1648	REPORT(kDebug, fPage, "StrokeLine start=[%f, %f], end=[%f, %f]",
1649		start.x, start.y, end.x, end.y);
1650
1651	SetColor();
1652	if (!MakesPDF())
1653		return;
1654
1655	if (IsClipping()) {
1656		BShape shape;
1657		shape.MoveTo(start);
1658		shape.LineTo(end);
1659		StrokeShape(&shape);
1660	} else {
1661		BeginTransparency();
1662		PDF_moveto(fPdf, tx(start.x), ty(start.y));
1663		PDF_lineto(fPdf, tx(end.x),   ty(end.y));
1664		StrokeOrClip();
1665		EndTransparency();
1666	}
1667}
1668
1669
1670void
1671PDFWriter::StrokeRect(BRect rect)
1672{
1673	REPORT(kDebug, fPage, "StrokeRect rect=[%f, %f, %f, %f]",
1674		rect.left, rect.top, rect.right, rect.bottom);
1675
1676	SetColor();
1677	if (!MakesPDF())
1678		return;
1679
1680	if (IsClipping()) {
1681		BShape shape;
1682		shape.MoveTo(BPoint(rect.left, rect.top));
1683		shape.LineTo(BPoint(rect.right, rect.top));
1684		shape.LineTo(BPoint(rect.right, rect.bottom));
1685		shape.LineTo(BPoint(rect.left, rect.bottom));
1686		shape.Close();
1687		StrokeShape(&shape);
1688	} else {
1689		BeginTransparency();
1690		PDF_rect(fPdf, tx(rect.left), ty(rect.bottom), scale(rect.Width()),
1691			scale(rect.Height()));
1692		StrokeOrClip();
1693		EndTransparency();
1694	}
1695}
1696
1697
1698void
1699PDFWriter::FillRect(BRect rect)
1700{
1701	REPORT(kDebug, fPage, "FillRect rect=[%f, %f, %f, %f]",
1702		rect.left, rect.top, rect.right, rect.bottom);
1703
1704	SetColor();
1705	if (!MakesPDF())
1706		return;
1707
1708	BeginTransparency();
1709	PDF_rect(fPdf, tx(rect.left), ty(rect.bottom), scale(rect.Width()),
1710		scale(rect.Height()));
1711	FillOrClip();
1712	EndTransparency();
1713}
1714
1715
1716/*!	The quarter ellipses in the corners of the rectangle are
1717	approximated with bezier curves.
1718	The constant 0.555... is taken from gobeProductive.
1719*/
1720void
1721PDFWriter::PaintRoundRect(BRect rect, BPoint radii, bool stroke)
1722{
1723	SetColor();
1724	if (!MakesPDF())
1725		return;
1726
1727	BPoint center;
1728
1729	float sx = radii.x;
1730	float sy = radii.y;
1731
1732	char str[256];
1733	sprintf(str, "PaintRoundRect sx %f sy %f", sx, sy);
1734	REPORT(kDebug, fPage, str);
1735
1736	float ax = sx;
1737	float bx = 0.5555555555555 * sx;
1738	float ay = sy;
1739	float by = 0.5555555555555 * sy;
1740
1741	center.x = rect.left + sx;
1742	center.y = rect.top + sy;
1743
1744	BShape shape;
1745	shape.MoveTo(BPoint(center.x - ax, center.y));
1746	BPoint a[3] = {
1747		BPoint(center.x - ax, center.y - by),
1748		BPoint(center.x - bx, center.y - ay),
1749		BPoint(center.x     , center.y - ay)};
1750	shape.BezierTo(a);
1751
1752	center.x = rect.right - sx;
1753	shape.LineTo(BPoint(center.x, center.y - ay));
1754
1755	BPoint b[3] = {
1756		BPoint(center.x + bx, center.y - ay),
1757		BPoint(center.x + ax, center.y - by),
1758		BPoint(center.x + ax, center.y)};
1759	shape.BezierTo(b);
1760
1761	center.y = rect.bottom - sy;
1762	shape.LineTo(BPoint(center.x + sx, center.y));
1763
1764	BPoint c[3] = {
1765		BPoint(center.x + ax, center.y + by),
1766		BPoint(center.x + bx, center.y + ay),
1767		BPoint(center.x     , center.y + ay)};
1768	shape.BezierTo(c);
1769
1770	center.x = rect.left + sx;
1771	shape.LineTo(BPoint(center.x, center.y + ay));
1772
1773	BPoint d[3] = {
1774		BPoint(center.x - bx, center.y + ay),
1775		BPoint(center.x - ax, center.y + by),
1776		BPoint(center.x - ax, center.y)};
1777	shape.BezierTo(d);
1778
1779	shape.Close();
1780
1781	PaintShape(&shape, stroke);
1782}
1783
1784
1785void
1786PDFWriter::StrokeRoundRect(BRect rect, BPoint radii)
1787{
1788	REPORT(kDebug, fPage, "StrokeRoundRect center=[%f, %f, %f, %f], "
1789		"radii=[%f, %f]", rect.left, rect.top, rect.right, rect.bottom, radii.x,
1790		radii.y);
1791	PaintRoundRect(rect, radii, kStroke);
1792}
1793
1794
1795void
1796PDFWriter::FillRoundRect(BRect rect, BPoint	radii)
1797{
1798	REPORT(kDebug, fPage, "FillRoundRect rect=[%f, %f, %f, %f], radii=[%f, %f]",
1799		rect.left, rect.top, rect.right, rect.bottom, radii.x, radii.y);
1800	PaintRoundRect(rect, radii, kFill);
1801}
1802
1803
1804void
1805PDFWriter::StrokeBezier(BPoint *control)
1806{
1807	REPORT(kDebug, fPage, "StrokeBezier");
1808	SetColor();
1809	if (!MakesPDF())
1810		return;
1811
1812	BShape shape;
1813	shape.MoveTo(control[0]);
1814	shape.BezierTo(&control[1]);
1815	StrokeShape(&shape);
1816}
1817
1818
1819void
1820PDFWriter::FillBezier(BPoint *control)
1821{
1822	REPORT(kDebug, fPage, "FillBezier");
1823	SetColor();
1824	if (!MakesPDF()) return;
1825	PDF_moveto(fPdf, tx(control[0].x), ty(control[0].y));
1826	PDF_curveto(fPdf, tx(control[1].x), ty(control[1].y),
1827		tx(control[2].x), ty(control[2].y), tx(control[3].x), ty(control[3].y));
1828	PDF_closepath(fPdf);
1829	FillOrClip();
1830}
1831
1832
1833/*!	Note the pen size is also scaled!
1834	We should approximate it with bezier curves too!
1835*/
1836void
1837PDFWriter::PaintArc(BPoint center, BPoint radii, float startTheta,
1838	float arcTheta, int stroke)
1839{
1840	float sx = scale(radii.x);
1841	float sy = scale(radii.y);
1842	float smax = sx > sy ? sx : sy;
1843
1844	SetColor();
1845	if (!MakesPDF()) return;
1846	if (IsClipping()); // TODO clip to line path
1847
1848	PDF_save(fPdf);
1849	PDF_scale(fPdf, sx, sy);
1850	PDF_setlinewidth(fPdf, fState->penSize / smax);
1851	PDF_arc(fPdf, tx(center.x) / sx, ty(center.y) / sy, 1, startTheta,
1852		startTheta + arcTheta);
1853	Paint(stroke);
1854	PDF_restore(fPdf);
1855}
1856
1857
1858void
1859PDFWriter::StrokeArc(BPoint center, BPoint radii, float startTheta,
1860	float arcTheta)
1861{
1862	REPORT(kDebug, fPage, "StrokeArc center=[%f, %f], radii=[%f, %f], "
1863		"startTheta=%f, arcTheta=%f", center.x, center.y, radii.x, radii.y,
1864		startTheta, arcTheta);
1865	PaintArc(center, radii, startTheta, arcTheta, true);
1866}
1867
1868
1869void
1870PDFWriter::FillArc(BPoint center, BPoint radii, float startTheta, float arcTheta)
1871{
1872	REPORT(kDebug, fPage, "FillArc center=[%f, %f], radii=[%f, %f], "
1873		"startTheta=%f, arcTheta=%f", center.x, center.y, radii.x, radii.y,
1874		startTheta, arcTheta);
1875	PaintArc(center, radii, startTheta, arcTheta, false);
1876}
1877
1878
1879void
1880PDFWriter::PaintEllipse(BPoint center, BPoint radii, bool stroke)
1881{
1882	float sx = radii.x;
1883	float sy = radii.y;
1884
1885	float ax = sx;
1886	float bx = 0.5555555555555 * sx;
1887	float ay = sy;
1888	float by = 0.5555555555555 * sy;
1889
1890	SetColor();
1891	if (!MakesPDF()) return;
1892
1893	BShape shape;
1894
1895	shape.MoveTo(BPoint(center.x - ax, center.y));
1896
1897	BPoint a[3] = {
1898		BPoint(center.x - ax, center.y - by),
1899		BPoint(center.x - bx, center.y - ay),
1900		BPoint(center.x     , center.y - ay) };
1901	shape.BezierTo(a);
1902
1903	BPoint b[3] = {
1904		BPoint(center.x + bx, center.y - ay),
1905		BPoint(center.x + ax, center.y - by),
1906		BPoint(center.x + ax, center.y)};
1907	shape.BezierTo(b);
1908
1909	BPoint c[3] = {
1910		BPoint(center.x + ax, center.y + by),
1911		BPoint(center.x + bx, center.y + ay),
1912		BPoint(center.x     , center.y + ay)};
1913	shape.BezierTo(c);
1914
1915	BPoint d[3] = {
1916		BPoint(center.x - bx, center.y + ay),
1917		BPoint(center.x - ax, center.y + by),
1918		BPoint(center.x - ax, center.y)};
1919	shape.BezierTo(d);
1920
1921	shape.Close();
1922
1923	PaintShape(&shape, stroke);
1924}
1925
1926
1927void
1928PDFWriter::StrokeEllipse(BPoint center, BPoint radii)
1929{
1930	REPORT(kDebug, fPage, "StrokeEllipse center=[%f, %f], radii=[%f, %f]",
1931		center.x, center.y, radii.x, radii.y);
1932	PaintEllipse(center, radii, true);
1933}
1934
1935
1936void
1937PDFWriter::FillEllipse(BPoint center, BPoint radii)
1938{
1939	REPORT(kDebug, fPage, "FillEllipse center=[%f, %f], radii=[%f, %f]",
1940		center.x, center.y, radii.x, radii.y);
1941	PaintEllipse(center, radii, false);
1942}
1943
1944
1945void
1946PDFWriter::StrokePolygon(int32 numPoints, BPoint *points, bool isClosed)
1947{
1948	int32 i;
1949	float x0, y0;
1950	REPORT(kDebug, fPage, "StrokePolygon numPoints=%ld, isClosed=%d\npoints=",
1951		numPoints, isClosed);
1952
1953	if (numPoints <= 1)
1954		return;
1955
1956	x0 = y0 = 0.0;
1957
1958	SetColor();
1959	if (!MakesPDF())
1960		return;
1961
1962	if (IsClipping()) {
1963		BShape shape;
1964		shape.MoveTo(*points);
1965		for (i = 1, points ++; i < numPoints; i++, points++) {
1966			shape.LineTo(*points);
1967		}
1968		if (isClosed)
1969			shape.Close();
1970		StrokeShape(&shape);
1971	} else {
1972		BeginTransparency();
1973		for ( i = 0; i < numPoints; i++, points++ ) {
1974			REPORT(kDebug, fPage, " [%f, %f]", points->x, points->y);
1975			if (i != 0) {
1976				PDF_lineto(fPdf, tx(points->x), ty(points->y));
1977			} else {
1978				x0 = tx(points->x);
1979				y0 = ty(points->y);
1980				PDF_moveto(fPdf, x0, y0);
1981			}
1982		}
1983		if (isClosed)
1984			PDF_lineto(fPdf, x0, y0);
1985		StrokeOrClip();
1986		EndTransparency();
1987	}
1988}
1989
1990
1991void
1992PDFWriter::FillPolygon(int32 numPoints, BPoint *points, bool isClosed)
1993{
1994	REPORT(kDebug, fPage, "FillPolygon numPoints=%ld, isClosed=%dpoints=",
1995		numPoints, isClosed);
1996
1997	SetColor();
1998	if (!MakesPDF())
1999		return;
2000
2001	BeginTransparency();
2002	for (int32 i = 0; i < numPoints; i++, points++ ) {
2003		REPORT(kDebug, fPage, " [%f, %f]", points->x, points->y);
2004		if (i != 0) {
2005			PDF_lineto(fPdf, tx(points->x), ty(points->y));
2006		} else {
2007			PDF_moveto(fPdf, tx(points->x), ty(points->y));
2008		}
2009	}
2010	PDF_closepath(fPdf);
2011	FillOrClip();
2012	EndTransparency();
2013}
2014
2015
2016void
2017PDFWriter::PaintShape(BShape *shape, bool stroke)
2018{
2019	if (stroke) StrokeShape(shape); else FillShape(shape);
2020}
2021
2022
2023void
2024PDFWriter::StrokeShape(BShape *shape)
2025{
2026	REPORT(kDebug, fPage, "StrokeShape");
2027	SetColor();
2028	if (!MakesPDF())
2029		return;
2030	BeginTransparency();
2031	DrawShape iterator(this, true);
2032	iterator.Iterate(shape);
2033	EndTransparency();
2034}
2035
2036
2037void
2038PDFWriter::FillShape(BShape *shape)
2039{
2040	REPORT(kDebug, fPage, "FillShape");
2041	SetColor();
2042	if (!MakesPDF()) return;
2043	BeginTransparency();
2044	DrawShape iterator(this, false);
2045	iterator.Iterate(shape);
2046	EndTransparency();
2047}
2048
2049
2050void
2051PDFWriter::ClipToPicture(BPicture *picture, BPoint point,
2052	bool clipToInversePicture)
2053{
2054	REPORT(kDebug, fPage, "ClipToPicture at (%f, %f) clip_to_inverse_picture "
2055		"= %s", point.x, point.y, clipToInversePicture ? "true" : "false");
2056
2057	if (!MakesPDF())
2058		return;
2059	if (clipToInversePicture) {
2060		REPORT(kError, fPage, "Clipping to inverse picture not implemented!");
2061		return;
2062	}
2063	if (fMode == kDrawingMode) {
2064		const bool set_origin = point.x != 0 || point.y != 0;
2065		PushInternalState();
2066		if (set_origin) {
2067			SetOrigin(point); PushInternalState();
2068		}
2069
2070		fMode = kClippingMode;
2071		// create subpath(s) for clipping
2072		Iterate(picture);
2073		fMode = kDrawingMode;
2074		// and clip to it/them
2075		PDF_clip(fPdf);
2076
2077		if (set_origin) {
2078			PopInternalState();
2079		}
2080		PopInternalState();
2081
2082		REPORT(kDebug, fPage, "Returning from ClipToPicture");
2083	} else {
2084		REPORT(kError, fPage,
2085			"Nested call of ClipToPicture not implemented yet!");
2086	}
2087}
2088
2089
2090void
2091PDFWriter::DrawPixels(BRect src, BRect dest, int32 width, int32 height,
2092	int32 bytesPerRow, int32 pixelFormat, int32 flags, void *data)
2093{
2094	REPORT(kDebug, fPage, "DrawPixels src=[%f, %f, %f, %f], dest=[%f, %f, %f, "
2095		"%f], width=%ld, height=%ld, bytesPerRow=%ld, pixelFormat=%ld, "
2096		"flags=%ld, data=%p", src.left, src.top, src.right, src.bottom,
2097		dest.left, dest.top, dest.right, dest.bottom, width, height, bytesPerRow,
2098		pixelFormat, flags, data);
2099
2100	SetColor();
2101
2102	if (IsClipping()) {
2103		REPORT(kError, fPage, "DrawPixels for clipping not implemented yet!");
2104		return;
2105	}
2106
2107	int maskId, image;
2108
2109	if (!GetImages(src, width, height, bytesPerRow, pixelFormat, flags, data,
2110			&maskId, &image)) {
2111		return;
2112	}
2113	if (!MakesPDF()) return;
2114
2115	const float scaleX = (dest.Width()+1) / (src.Width()+1);
2116	const float scaleY = (dest.Height()+1) / (src.Height()+1);
2117
2118	const bool needs_scaling = scaleX != 1.0 || scaleY != 1.0;
2119
2120	if (needs_scaling) {
2121		PDF_save(fPdf);
2122		PDF_scale(fPdf, scaleX, scaleY);
2123	}
2124
2125	// This seems to work with Gobe Productive, why?
2126	// Can use Begin/EndTransparency if the alpha value for each pixel
2127	// is the same.
2128	// Otherwise we need "SoftMasks". Don't know if PDFlib 5.x already
2129	// supports them.
2130	BeginTransparency();
2131
2132	float x = tx(dest.left)   / scaleX;
2133	float y = ty(dest.bottom) / scaleY;
2134
2135	if ( image >= 0 ) {
2136		PDF_place_image(fPdf, image, x, y, scale(1.0));
2137#if !USE_IMAGE_CACHE
2138		PDF_close_image(fPdf, image);
2139#endif
2140	} else {
2141		REPORT(kError, fPage, "PDF_open_image_file failed!");
2142	}
2143
2144#if !USE_IMAGE_CACHE
2145	if (maskId != -1) PDF_close_image(fPdf, maskId);
2146#endif
2147	EndTransparency();
2148
2149	if (needs_scaling) PDF_restore(fPdf);
2150}
2151
2152
2153void
2154PDFWriter::SetClippingRects(BRect *rects, uint32 numRects)
2155{
2156	uint32	i;
2157
2158	REPORT(kDebug, fPage, "SetClippingRects numRects=%ld\nrects=", \
2159			numRects);
2160
2161	if (!MakesPDF()) return;
2162
2163	for ( i = 0; i < numRects; i++, rects++ ) {
2164		REPORT(kDebug, fPage, " [%f, %f, %f, %f]", \
2165				rects->left, rects->top, rects->right, rects->bottom);
2166		PDF_moveto(fPdf, tx(rects->left),  ty(rects->top));
2167		PDF_lineto(fPdf, tx(rects->right), ty(rects->top));
2168		PDF_lineto(fPdf, tx(rects->right), ty(rects->bottom));
2169		PDF_lineto(fPdf, tx(rects->left),  ty(rects->bottom));
2170		PDF_closepath(fPdf);
2171	}
2172	if (numRects > 0) PDF_clip(fPdf);
2173}
2174
2175
2176void
2177PDFWriter::PushState()
2178{
2179	REPORT(kDebug, fPage, "PushState");
2180	PushInternalState();
2181//	LOG((fLog, "height = %f x0 = %f y0 = %f", fState->height, fState->x0, fState->y0));
2182	if (!MakesPDF()) return;
2183	PDF_save(fPdf);
2184}
2185
2186
2187void
2188PDFWriter::PopState()
2189{
2190	REPORT(kDebug, fPage, "PopState");
2191	if (PopInternalState()) {
2192		if (!MakesPDF()) return;
2193		PDF_restore(fPdf);
2194	}
2195}
2196
2197
2198void
2199PDFWriter::EnterStateChange()
2200{
2201	REPORT(kDebug, fPage, "EnterStateChange");
2202	// nothing to do
2203}
2204
2205
2206void
2207PDFWriter::ExitStateChange()
2208{
2209	REPORT(kDebug, fPage, "ExitStateChange");
2210	// nothing to do
2211}
2212
2213
2214void
2215PDFWriter::EnterFontState()
2216{
2217	REPORT(kDebug, fPage, "EnterFontState");
2218	// nothing to do
2219}
2220
2221
2222void
2223PDFWriter::ExitFontState()
2224{
2225	REPORT(kDebug, fPage, "ExitFontState");
2226	// nothing to do
2227}
2228
2229
2230void
2231PDFWriter::SetOrigin(BPoint pt)
2232{
2233	REPORT(kDebug, fPage, "SetOrigin pt=[%f, %f]", pt.x, pt.y);
2234
2235	// XXX scale pt with current scaling factor or with
2236	//     scaling factor of previous state? (fState->prev->scale)
2237	BPoint o = fState->prev->pdfSystem.Origin();
2238	pdfSystem()->SetOrigin(
2239		o.x + pdfSystem()->scale(pt.x),
2240		o.y + pdfSystem()->scale(pt.y)
2241	);
2242}
2243
2244
2245void
2246PDFWriter::SetPenLocation(BPoint pt)
2247{
2248	REPORT(kDebug, fPage, "SetPenLocation pt=[%f, %f]", pt.x, pt.y);
2249
2250	fState->penX = pt.x;
2251	fState->penY = pt.y;
2252}
2253
2254
2255void
2256PDFWriter::SetDrawingMode(drawing_mode mode)
2257{
2258	REPORT(kDebug, fPage, "SetDrawingMode mode=%d", mode);
2259	fState->drawingMode = mode;
2260}
2261
2262
2263void
2264PDFWriter::SetLineMode(cap_mode capMode, join_mode joinMode, float miterLimit)
2265{
2266	REPORT(kDebug, fPage, "SetLineMode");
2267	fState->capMode    = capMode;
2268	fState->joinMode   = joinMode;
2269	fState->miterLimit = miterLimit;
2270	if (!MakesPDF()) return;
2271	int m = 0;
2272	switch (capMode) {
2273		case B_BUTT_CAP:   m = 0; break;
2274		case B_ROUND_CAP:  m = 1; break;
2275		case B_SQUARE_CAP: m = 2; break;
2276	}
2277	PDF_setlinecap(fPdf, m);
2278
2279	m = 0;
2280	switch (joinMode) {
2281		case B_MITER_JOIN: m = 0; break;
2282		case B_ROUND_JOIN: m = 1; break;
2283		case B_BUTT_JOIN: // fall through TODO: check this; no equivalent in PDF?
2284		case B_SQUARE_JOIN: // fall through TODO: check this too
2285		case B_BEVEL_JOIN: m = 2; break;
2286	}
2287	PDF_setlinejoin(fPdf, m);
2288
2289	PDF_setmiterlimit(fPdf, miterLimit);
2290
2291}
2292
2293
2294void
2295PDFWriter::SetPenSize(float size)
2296{
2297	REPORT(kDebug, fPage, "SetPenSize size=%f", size);
2298	if (size <= 0.00001)
2299		size = 1;
2300
2301	// TODO: scaling required?
2302	fState->penSize = scale(size);
2303	if (!MakesPDF())
2304		return;
2305
2306	PDF_setlinewidth(fPdf, size);
2307}
2308
2309
2310void
2311PDFWriter::SetForeColor(rgb_color color)
2312{
2313	//if (IsClipping()) return; // ignore
2314
2315	float red = color.red / 255.0;
2316	float green = color.green / 255.0;
2317	float blue = color.blue / 255.0;
2318
2319	REPORT(kDebug, fPage, "SetForColor color=[%d, %d, %d, %d] -> [%f, %f, %f]",
2320		color.red, color.green, color.blue, color.alpha, red, green, blue);
2321
2322	fState->foregroundColor = color;
2323}
2324
2325
2326void
2327PDFWriter::SetBackColor(rgb_color color)
2328{
2329	//if (IsClipping()) return; // ignore
2330
2331	float red, green, blue;
2332
2333	red 	= color.red / 255.0;
2334	green 	= color.green / 255.0;
2335	blue 	= color.blue / 255.0;
2336
2337	REPORT(kDebug, fPage, "SetBackColor color=[%d, %d, %d, %d] -> [%f, %f, %f]",
2338		color.red, color.green, color.blue, color.alpha, red, green, blue);
2339
2340	fState->backgroundColor = color;
2341}
2342
2343
2344void
2345PDFWriter::SetStipplePattern(pattern pat)
2346{
2347	REPORT(kDebug, fPage, "SetStipplePattern");
2348	fState->pattern0 = pat;
2349}
2350
2351
2352void
2353PDFWriter::SetScale(float scale)
2354{
2355	REPORT(kDebug, fPage, "SetScale scale=%f", scale);
2356	pdfSystem()->SetScale(scale * fState->prev->pdfSystem.Scale());
2357}
2358
2359
2360void
2361PDFWriter::SetFontFamily(char *family)
2362{
2363	REPORT(kDebug, fPage, "SetFontFamily family=\"%s\"", family);
2364
2365	fState->beFont.SetFamilyAndStyle(family, NULL);
2366}
2367
2368
2369void
2370PDFWriter::SetFontStyle(char *style)
2371{
2372	REPORT(kDebug, fPage, "SetFontStyle style=\"%s\"", style);
2373
2374	fState->beFont.SetFamilyAndStyle(NULL, style);
2375}
2376
2377
2378void
2379PDFWriter::SetFontSpacing(int32 spacing)
2380{
2381	REPORT(kDebug, fPage, "SetFontSpacing spacing=%ld", spacing);
2382	// XXX scaling required?
2383	// if it is, do it when the font is used...
2384	fState->beFont.SetSpacing(spacing);
2385}
2386
2387
2388void
2389PDFWriter::SetFontSize(float size)
2390{
2391	REPORT(kDebug, fPage, "SetFontSize size=%f", size);
2392
2393	fState->beFont.SetSize(size);
2394}
2395
2396
2397void
2398PDFWriter::SetFontRotate(float rotation)
2399{
2400	REPORT(kDebug, fPage, "SetFontRotate rotation=%f", rotation);
2401	fState->beFont.SetRotation(RAD2DEGREE(rotation));
2402}
2403
2404
2405void
2406PDFWriter::SetFontEncoding(int32 encoding)
2407{
2408	REPORT(kDebug, fPage, "SetFontEncoding encoding=%ld", encoding);
2409	fState->beFont.SetEncoding(encoding);
2410}
2411
2412
2413void
2414PDFWriter::SetFontFlags(int32 flags)
2415{
2416	REPORT(kDebug, fPage, "SetFontFlags flags=%ld (0x%lx)", flags, flags);
2417	fState->beFont.SetFlags(flags);
2418}
2419
2420
2421void
2422PDFWriter::SetFontShear(float shear)
2423{
2424	REPORT(kDebug, fPage, "SetFontShear shear=%f", shear);
2425	fState->beFont.SetShear(shear);
2426}
2427
2428
2429void
2430PDFWriter::SetFontFace(int32 flags)
2431{
2432	REPORT(kDebug, fPage, "SetFontFace flags=%ld (0x%lx)", flags, flags);
2433//	fState->beFont.SetFace(flags);
2434}
2435
2436
2437//	#pragma mark Redirectors to instance callbacks/handlers
2438
2439
2440size_t
2441_WriteData(PDF *pdf, void *data, size_t size)
2442{
2443	return ((PDFWriter *)PDF_get_opaque(pdf))->WriteData(data, size);
2444}
2445
2446
2447void
2448_ErrorHandler(PDF *pdf, int type, const char *msg)
2449{
2450	return ((PDFWriter *)PDF_get_opaque(pdf))->ErrorHandler(type, msg);
2451}
2452
2453