1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include <QCryptographicHash>
9 
10 #include "pdfwriter.h"
11 #include "rc4.h"
12 #include "scstreamfilter_rc4.h"
13 #include "util.h"
14 
15 namespace Pdf
16 {
17 
18 
isWhiteSpace(char c)19 	bool isWhiteSpace(char c)
20 	{
21 		switch (c)
22 		{
23 			case 0:
24 			case 9:
25 			case 10:
26 			case 12:
27 			case 13:
28 			case 32:
29 				return true;
30 			default:
31 				return false;
32 		}
33 	}
34 
isDelimiter(char c)35 	bool isDelimiter(char c)
36 	{
37 		QByteArray delims("()<>[]{}/%");
38 		return delims.indexOf(c) >= 0;
39 	}
40 
isRegular(char c)41 	bool isRegular(char c)
42 	{
43 		return !isWhiteSpace(c) && !isDelimiter(c);
44 	}
45 
toPdfDocEncoding(QChar c)46 	uchar toPdfDocEncoding(QChar c)
47 	{
48 		uchar row = c.row();
49 		uchar cell = c.cell();
50 		switch (row)
51 		{
52 			case 0:
53 				if (cell <= 23
54 					|| (cell >= 32 && cell <= 127)
55 					|| cell >= 161)
56 				{
57 					return cell;
58 				}
59 				else
60 				{
61 					return 0;
62 				}
63 				break;
64 			case 0x01:
65 				switch (cell)
66 				{
67 					case 0x92:
68 						return (134);
69 					case 0x41:
70 						return (149);
71 					case 0x52:
72 						return (150);
73 					case 0x60:
74 						return (151);
75 					case 0x78:
76 						return (152);
77 					case 0x7d:
78 						return (153);
79 					case 0x31:
80 						return (154);
81 					case 0x42:
82 						return (155);
83 					case 0x53:
84 						return (156);
85 					case 0x61:
86 						return (157);
87 					case 0x7e:
88 						return (158);
89 					default:
90 						return (0);
91 						break;
92 				}
93 				break;
94 
95 			case 0x02:
96 				switch (cell)
97 				{
98 					case 0xc7:
99 						return (25);
100 					case 0xc6:
101 						return (26);
102 					case 0xd8:
103 						return (24);
104 					case 0xd9:
105 						return (27);
106 					case 0xda:
107 						return (30);
108 					case 0xdb:
109 						return (29);
110 					case 0xdc:
111 						return (31);
112 					case 0xdd:
113 						return (28);
114 					default:
115 						return (0);
116 						break;
117 				}
118 				break;
119 
120 			case 0x20:
121 				switch (cell)
122 				{
123 					case 0x13:
124 						return (133);
125 					case 0x14:
126 						return (132);
127 					case 0x18:
128 						return (143);
129 					case 0x19:
130 						return (144);
131 					case 0x1a:
132 						return (145);
133 					case 0x1c:
134 						return (141);
135 					case 0x1d:
136 						return (142);
137 					case 0x1e:
138 						return (140);
139 					case 0x20:
140 						return (129);
141 					case 0x21:
142 						return (130);
143 					case 0x22:
144 						return (128);
145 					case 0x26:
146 						return (131);
147 					case 0x30:
148 						return (139);
149 					case 0x39:
150 						return (136);
151 					case 0x3a:
152 						return (137);
153 					case 0x44:
154 						return (135);
155 					case 0xac:
156 						return (160);
157 
158 					default:
159 						return (0);
160 						break;
161 				}
162 				break;
163 
164 			case 0x21:
165 				switch (cell)
166 				{
167 					case 0x22:
168 						return (146);
169 					default:
170 						return (0);
171 						break;
172 				}
173 				break;
174 
175 			case 0x22:
176 				switch (cell)
177 				{
178 					case 0x12:
179 						return (138);
180 					default:
181 						return (0);
182 						break;
183 				}
184 				break;
185 
186 			case 0xfb:
187 				switch (cell)
188 				{
189 					case 0x01:
190 						return (147);
191 					case 0x02:
192 						return (148);
193 					default:
194 						return (0);
195 						break;
196 				}
197 				break;
198 		}
199 
200 		return 0;
201 	}
202 
toPdfDocEncoding(QString s)203 	QByteArray toPdfDocEncoding(QString s)
204 	{
205 		QByteArray result;
206 		for (int i = 0; i < s.length(); ++i)
207 		{
208 			uchar pdfChar = toPdfDocEncoding(s[i]);
209 			if ((pdfChar != 0) || s[i].isNull())
210 				result += pdfChar;
211 			else
212 				result += "?";
213 		}
214 		return result;
215 	}
216 
fromPDFDocEncoding(int code)217 	int fromPDFDocEncoding(int code)
218 	{
219 		if (code <= 23
220 			|| (code >= 32 && code <= 127)
221 			|| (code >= 161))
222 		{
223 			return code;
224 		}
225 
226 		if (code == 24)  return 0x02d8;
227 		if (code == 25)  return 0x02c7;
228 		if (code == 26)  return 0x02c6;
229 		if (code == 27)  return 0x02d9;
230 		if (code == 28)  return 0x02dd;
231 		if (code == 29)  return 0x02db;
232 		if (code == 30)  return 0x02da;
233 		if (code == 31)  return 0x02dc;
234 
235 		if (code == 128) return 0x2022;
236 		if (code == 129) return 0x2020;
237 
238 		if (code == 130) return 0x2021;
239 		if (code == 131) return 0x2026;
240 		if (code == 132) return 0x2014;
241 		if (code == 133) return 0x2013;
242 		if (code == 134) return 0x0192;
243 		if (code == 135) return 0x2044;
244 		if (code == 136) return 0x2039;
245 		if (code == 137) return 0x203a;
246 		if (code == 138) return 0x2212;
247 		if (code == 139) return 0x2030;
248 
249 		if (code == 140) return 0x201e;
250 		if (code == 141) return 0x201c;
251 		if (code == 142) return 0x201d;
252 		if (code == 143) return 0x2018;
253 		if (code == 144) return 0x2019;
254 		if (code == 145) return 0x201a;
255 		if (code == 146) return 0x2122;
256 		if (code == 147) return 0xfb01;
257 		if (code == 148) return 0xfb02;
258 		if (code == 149) return 0x0141;
259 
260 		if (code == 150) return 0x0152;
261 		if (code == 151) return 0x0160;
262 		if (code == 152) return 0x0178;
263 		if (code == 153) return 0x017d;
264 		if (code == 154) return 0x0131;
265 		if (code == 155) return 0x0142;
266 		if (code == 156) return 0x0153;
267 		if (code == 157) return 0x0161;
268 		if (code == 158) return 0x017e;
269 
270 		if (code == 160) return 0x20ac;
271 
272 		return 0;
273 	}
274 
toUTF16(QString s)275 	QByteArray toUTF16(QString s)
276 	{
277 		QByteArray result;
278 		result.append('\xfe');
279 		result.append('\xff');
280 		for (int i = 0; i < s.length(); ++i)
281 		{
282 			result.append(s[i].row());
283 			result.append(s[i].cell());
284 		}
285 		return result;
286 	}
287 
toAscii(QString s)288 	QByteArray toAscii(QString s)
289 	{
290 		QByteArray result;
291 		for (int i = 0; i < s.length(); ++i)
292 		{
293 			uchar row = s[i].row();
294 			uchar cell = s[i].cell();
295 			if (row == 0 && cell <= 127)
296 			{
297 				result.append(cell);
298 			}
299 			else
300 			{
301 				result.append("?");
302 			}
303 		}
304 		return result;
305 	}
306 
toPdf(bool v)307 	QByteArray toPdf(bool v)
308 	{
309 		return v? "true" : "false";
310 	}
311 
toPdf(int v)312 	QByteArray toPdf(int v)
313 	{
314 		return QByteArray::number(v);
315 	}
316 
toPdf(uint v)317 	QByteArray toPdf(uint v)
318 	{
319 		return QByteArray::number(v);
320 	}
321 
toPdf(qlonglong v)322 	QByteArray toPdf(qlonglong v)
323 	{
324 		return QByteArray::number(v);
325 	}
326 
toPdf(qulonglong v)327 	QByteArray toPdf(qulonglong v)
328 	{
329 		return QByteArray::number(v);
330 	}
331 
toPdf(double v)332 	QByteArray toPdf(double v)
333 	{
334 		return QByteArray::number(v, 'f');
335 	}
336 
toObjRef(PdfId id)337 	QByteArray toObjRef(PdfId id)
338 	{
339 		return toPdf(id) + " 0 R";
340 	}
341 
342 
toLiteralString(const QString & s)343 	QByteArray toLiteralString(const QString& s)
344 	{
345 		return toLiteralString(toPdfDocEncoding(s));
346 	}
347 
toLiteralString(QByteArray s)348 	QByteArray toLiteralString(QByteArray s)
349 	{
350 		int linelength = 80;
351 		QByteArray result("(");
352 		for (int i = 0; i < s.length(); ++i)
353 		{
354 			uchar v = s[i];
355 			if ( v == '(' || v == ')' || v == '\\')
356 			{
357 				result.append('\\');
358 				result.append(v);
359 			}
360 			else if (v < 32 || v >= 127)
361 			{
362 				result.append('\\');
363 				result.append("01234567"[ (v/64) % 8]);
364 				result.append("01234567"[ (v/8) % 8]);
365 				result.append("01234567"[ v % 8]);
366 			}
367 			else
368 			{
369 				result.append(v);
370 			}
371 			if (i % linelength == linelength-1)
372 				result.append("\\\n");
373 		}
374 		result.append(")");
375 		return result;
376 	}
377 
toHexString(QByteArray s)378 	QByteArray toHexString(QByteArray s)
379 	{
380 		int linelength = 80;
381 		QByteArray result("<");
382 		for (int i = 0; i < s.length(); ++i)
383 		{
384 			uchar v = s[i];
385 			result.append("0123456789ABCDEF"[v / 16]);
386 			result.append("0123456789ABCDEF"[v % 16]);
387 			if (i % linelength == linelength-1)
388 				result.append("\n");
389 		}
390 		result.append(">");
391 		return result;
392 	}
393 
toHexString8(quint8 b)394 	QByteArray toHexString8(quint8 b)
395 	{
396 		QByteArray result("<");
397 		result.append("0123456789ABCDEF"[b / 16]);
398 		result.append("0123456789ABCDEF"[b % 16]);
399 		result.append(">");
400 		return result;
401 	}
402 
toHexString16(quint16 b)403 	QByteArray toHexString16(quint16 b)
404 	{
405 		QByteArray result("<");
406 		result.append("0123456789ABCDEF"[(b >> 12) & 0xf]);
407 		result.append("0123456789ABCDEF"[(b >> 8) & 0xf]);
408 		result.append("0123456789ABCDEF"[(b >> 4) & 0xf]);
409 		result.append("0123456789ABCDEF"[ b & 0xf]);
410 		result.append(">");
411 		return result;
412 	}
413 
toHexString32(quint32 b)414 	QByteArray toHexString32(quint32 b)
415 	{
416 		QByteArray result("<");
417 		result.append("0123456789ABCDEF"[(b >> 28) & 0xf]);
418 		result.append("0123456789ABCDEF"[(b >> 24) & 0xf]);
419 		result.append("0123456789ABCDEF"[(b >> 20) & 0xf]);
420 		result.append("0123456789ABCDEF"[(b >> 16) & 0xf]);
421 		result.append("0123456789ABCDEF"[(b >> 12) & 0xf]);
422 		result.append("0123456789ABCDEF"[(b >> 8) & 0xf]);
423 		result.append("0123456789ABCDEF"[(b >> 4) & 0xf]);
424 		result.append("0123456789ABCDEF"[ b & 0xf]);
425 		result.append(">");
426 		return result;
427 	}
428 
toName(const QString & s)429 	QByteArray toName(const QString& s)
430 	{
431 		return toName(toPdfDocEncoding(s));
432 	}
433 
toName(QByteArray s)434 	QByteArray toName(QByteArray s)
435 	{
436 		QByteArray result("/");
437 		for (int i = 0; i < s.length(); ++i)
438 		{
439 			uchar c = s[i];
440 			if (c <= 32 || c >= 127 || c == '#' || isDelimiter(c))
441 			{
442 				result.append("#");
443 				result.append("0123456789ABCDEF"[c / 16]);
444 				result.append("0123456789ABCDEF"[c % 16]);
445 			}
446 			else
447 			{
448 				result.append(c);
449 			}
450 		}
451 		return result;
452 	}
453 
toDateString(const QDateTime & dt)454 	QByteArray toDateString(const QDateTime& dt)
455 	{
456 		QString tmp = dt.toString("yyyy:MM:dd:HH:mm:ss");
457 		tmp = tmp.replace(":", "");
458 		return "D:" + tmp.toLatin1() + "Z";
459 	}
460 
toRectangleArray(QRect r)461 	QByteArray toRectangleArray(QRect r)
462 	{
463 		return "[" + toPdf(r.left()) + " " + toPdf(r.bottom()) + " " + toPdf(r.right()) + " " + toPdf(r.top()) + "]";
464 	}
465 
toRectangleArray(QRectF r)466 	QByteArray toRectangleArray(QRectF r)
467 	{
468 		return "[" + toPdf(r.left()) + " " + toPdf(r.bottom()) + " " + toPdf(r.right()) + " " + toPdf(r.top()) + "]";
469 	}
470 
Writer()471 	Writer::Writer()
472 	{
473 		CatalogObj = 1;
474 		InfoObj = 2;
475 		PagesObj = 3;
476 		AcroFormObj = 0;
477 		DestsObj = 0;
478 		OutlinesObj = 0;
479 		NamesObj = 0;
480 		ThreadsObj = 0;
481 
482 		OCPropertiesObj = 0;
483 		OutputIntentObj = 0;
484 		EncryptObj = 0;
485 		MetaDataObj = 0;
486 		ResourcesObj = 0;
487 		OpenActionObj = 0;
488 
489 		m_ObjCounter = 0;
490 		m_CurrentObj = 0;
491 
492 		m_KeyLen = 5;
493 		m_KeyGen.resize(32);
494 		m_OwnerKey.resize(32);
495 		m_UserKey.resize(32);
496 		m_FileID.resize(16);
497 		m_EncryKey.resize(5);
498 
499 		int kg_array[] = {
500 		    0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
501 		    0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
502 		    0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,
503 		    0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a };
504 		for (int a = 0; a < 32; ++a)
505 			m_KeyGen[a] = kg_array[a];
506 	}
507 
508 
open(const QString & fn)509 	bool Writer::open(const QString& fn)
510 	{
511 		m_Spool.setFileName(fn);
512 		if (!m_Spool.open(QIODevice::WriteOnly))
513 			return false;
514 		m_outStream.setDevice(&m_Spool);
515 		m_ObjCounter = 4;
516 		return true;
517 	}
518 
openStreamFilter(bool encrypted,PdfId objId)519 	ScStreamFilter* Writer::openStreamFilter(bool encrypted, PdfId objId)
520 	{
521 		if (encrypted)
522 		{
523 			QByteArray step1 = ComputeRC4Key(objId);
524 			return new ScRC4EncodeFilter(&m_outStream, step1.data(), qMin(m_KeyLen+5, 16));
525 		}
526 		return new ScNullEncodeFilter(&m_outStream);
527 	}
528 
close(bool abortExport)529 	bool Writer::close(bool abortExport)
530 	{
531 		bool result = (m_Spool.error() == QFile::NoError);
532 
533 		m_Spool.close();
534 		if (abortExport || !result)
535 		{
536 			if (m_Spool.exists())
537 				m_Spool.remove();
538 		}
539 		return result;
540 	}
541 
setFileId(const QByteArray & id)542 	void Writer::setFileId(const QByteArray& id)
543 	{
544 		m_FileID = QCryptographicHash::hash(id, QCryptographicHash::Md5);
545 	}
546 
setEncryption(bool keyLen16,const QByteArray & PassOwner,const QByteArray & PassUser,int Permissions)547 	void Writer::setEncryption(bool keyLen16, const QByteArray& PassOwner, const QByteArray& PassUser, int Permissions)
548 	{
549 		QByteArray ok = "";
550 		QByteArray uk = "";
551 
552 		if (keyLen16)
553 			m_KeyLen = 16;
554 		else
555 			m_KeyLen = 5;
556 		CalcOwnerKey(PassOwner, PassUser);
557 		CalcUserKey(PassUser, Permissions);
558 		for (uint cl2 = 0; cl2 < 32; ++cl2)
559 			ok += (m_OwnerKey[cl2]);
560 		if (keyLen16)
561 		{
562 			for (uint cl3 = 0; cl3 < 16; ++cl3)
563 				uk += (m_UserKey[cl3]);
564 			for (uint cl3r = 0; cl3r < 16; ++cl3r)
565 				uk += (m_KeyGen[cl3r]);
566 		}
567 		else
568 		{
569 			for (uint cl = 0; cl < 32; ++cl)
570 				uk += (m_UserKey[cl]);
571 		}
572 
573 		EncryptObj = newObject();
574 		startObj(EncryptObj);
575 		write("<<\n/Filter /Standard\n");
576 		write( keyLen16 ? "/R 3\n/V 2\n/Length 128\n" : "/R 2\n/V 1\n");
577 		write("/O "+Pdf::toHexString(ok)+"\n");
578 		write("/U "+Pdf::toHexString(uk)+"\n");
579 		write("/P "+Pdf::toPdf(Permissions)+"\n>>");
580 		endObj(EncryptObj);
581 	}
582 
encryptBytes(const QByteArray & in,PdfId ObjNum)583 	QByteArray Writer::encryptBytes(const QByteArray& in, PdfId ObjNum)
584 	{
585 		rc4_context_t rc4;
586 		QByteArray result(in.length(), ' ');
587 		if (in.length() > 0)
588 		{
589 			QByteArray step1 = ComputeRC4Key(ObjNum);
590 			rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(m_KeyLen+5, 16));
591 			rc4_encrypt(&rc4, reinterpret_cast<const uchar*>(in.data()), reinterpret_cast<uchar*>(result.data()), in.length());
592 		}
593 		return result;
594 	}
595 
ComputeRC4Key(PdfId ObjNum)596 	QByteArray Writer::ComputeRC4Key(PdfId ObjNum)
597 	{
598 		int dlen = 0;
599 		QByteArray data(10, ' ');
600 		if (m_KeyLen > 5)
601 			data.resize(21);
602 		for (int cd = 0; cd < m_KeyLen; ++cd)
603 		{
604 			data[cd] = m_EncryKey[cd];
605 			dlen++;
606 		}
607 		data[dlen++] = ObjNum;
608 		data[dlen++] = ObjNum >> 8;
609 		data[dlen++] = ObjNum >> 16;
610 		data[dlen++] = 0;
611 		data[dlen++] = 0;
612 		QByteArray rc4Key(16, ' ');
613 		rc4Key = QCryptographicHash::hash(data, QCryptographicHash::Md5);
614 		rc4Key.resize(qMin(m_KeyLen+5, 16));
615 		return rc4Key;
616 	}
617 
FitKey(const QByteArray & pass)618 	QByteArray Writer::FitKey(const QByteArray & pass)
619 	{
620 		QByteArray pw(pass);
621 		if (pw.length() < 32)
622 		{
623 			uint l = pw.length();
624 			for (uint a = 0; a < 32 - l; ++a)
625 				pw += (m_KeyGen[a]);
626 		}
627 		else
628 			pw = pw.left(32);
629 		return pw;
630 	}
631 
CalcOwnerKey(const QByteArray & Owner,const QByteArray & User)632 	void Writer::CalcOwnerKey(const QByteArray & Owner, const QByteArray & User)
633 	{
634 		rc4_context_t rc4;
635 		QByteArray pw(FitKey(User));
636 		QByteArray pw2(FitKey(Owner.isEmpty() ? User : Owner));
637 		QByteArray step1(16, ' ');
638 		step1 = QCryptographicHash::hash(pw2, QCryptographicHash::Md5);
639 		if (m_KeyLen > 5)
640 		{
641 			for (int kl = 0; kl < 50; ++kl)
642 				step1 = QCryptographicHash::hash(step1, QCryptographicHash::Md5);
643 		}
644 		QByteArray us(32, ' ');
645 		QByteArray enk(16, ' ');
646 		if (m_KeyLen > 5)
647 		{
648 			for (uint a2 = 0; a2 < 32; ++a2)
649 				m_OwnerKey[a2] = QChar(pw.at(a2)).cell();
650 			for (int rl = 0; rl < 20; rl++)
651 			{
652 				for (int j = 0; j < 16; j ++)
653 					enk[j] = step1[j] ^ rl;
654 				rc4_init(&rc4, reinterpret_cast<uchar*>(enk.data()), 16);
655 				rc4_encrypt(&rc4, reinterpret_cast<uchar*>(m_OwnerKey.data()),
656 							reinterpret_cast<uchar*>(m_OwnerKey.data()), 32);
657 			}
658 		}
659 		else
660 		{
661 			for (uint a = 0; a < 32; ++a)
662 				us[a] = static_cast<uchar>(QChar(pw.at(a)).cell());
663 			rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), 5);
664 			rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()),
665 						reinterpret_cast<uchar*>(m_OwnerKey.data()), 32);
666 		}
667 	}
668 
CalcUserKey(const QByteArray & User,int Permission)669 	void Writer::CalcUserKey(const QByteArray & User, int Permission)
670 	{
671 		rc4_context_t	rc4;
672 		QByteArray pw(FitKey(User));
673 		QByteArray step1(16, ' ');
674 		QByteArray perm(4, ' ');
675 		uint perm_value = static_cast<uint>(Permission);
676 		perm[0] = perm_value;
677 		perm[1] = perm_value >> 8;
678 		perm[2] = perm_value >> 16;
679 		perm[3] = perm_value >> 24;
680 		for (uint a = 0; a < 32; ++a)
681 			pw += (m_OwnerKey[a]);
682 		for (uint a1 = 0; a1 < 4; ++a1)
683 			pw += (perm[a1]);
684 		for (uint a3 = 0; a3 < 16; ++a3)
685 			pw += (m_FileID[a3]);
686 		step1 = QCryptographicHash::hash(pw, QCryptographicHash::Md5);
687 		if (m_KeyLen > 5)
688 		{
689 			for (int kl = 0; kl < 50; ++kl)
690 				step1 = QCryptographicHash::hash(step1, QCryptographicHash::Md5);
691 			m_EncryKey.resize(16);
692 		}
693 		for (int a2 = 0; a2 < m_KeyLen; ++a2)
694 			m_EncryKey[a2] = step1[a2];
695 		if (m_KeyLen > 5)
696 		{
697 			QByteArray pr2("");
698 			for (int kl3 = 0; kl3 < 32; ++kl3)
699 				pr2 += (m_KeyGen[kl3]);
700 			for (uint a4 = 0; a4 < 16; ++a4)
701 				pr2 += (m_FileID[a4]);
702 			step1 = QCryptographicHash::hash(pr2, QCryptographicHash::Md5);
703 			QByteArray enk(16, ' ');
704 			for (uint a3 = 0; a3 < 16; ++a3)
705 				m_UserKey[a3] = step1[a3];
706 			for (int rl = 0; rl < 20; rl++)
707 			{
708 				for (int j = 0; j < 16; j ++)
709 					enk[j] = m_EncryKey[j] ^ rl;
710 				rc4_init(&rc4, reinterpret_cast<uchar*>(enk.data()), 16);
711 				rc4_encrypt(&rc4, reinterpret_cast<uchar*>(m_UserKey.data()), reinterpret_cast<uchar*>(m_UserKey.data()), 16);
712 			}
713 		}
714 		else
715 		{
716 			rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), 5);
717 			rc4_encrypt(&rc4, reinterpret_cast<uchar*>(m_KeyGen.data()), reinterpret_cast<uchar*>(m_UserKey.data()), 32);
718 		}
719 	}
720 
721 //	QByteArray PDFLibCore::ComputeMD5(const QString& in)
722 //	{
723 //		uint inlen=in.length();
724 //		QByteArray TBytes(inlen, ' ');
725 //		for (uint a = 0; a < inlen; ++a)
726 //			TBytes[a] = static_cast<uchar>(QChar(in.at(a)).cell());
727 //		return QCryptographicHash::hash(TBytes, QCryptographicHash::Md5);
728 //	}
729 
writeHeader(const PDFVersion & Version)730 	void Writer::writeHeader(const PDFVersion& Version)
731 	{
732 		switch (Version)
733 		{
734 			case PDFVersion::PDF_X1a:
735 			case PDFVersion::PDF_X3:
736 			case PDFVersion::PDF_13:
737 				write("%PDF-1.3\n");
738 				break;
739 			case PDFVersion::PDF_14:
740 				write("%PDF-1.4\n");
741 				break;
742 			case PDFVersion::PDF_15:
743 				write("%PDF-1.5\n");
744 				break;
745 			case PDFVersion::PDF_16:
746 			case PDFVersion::PDF_X4:
747 				write("%PDF-1.6\n");
748 				break;
749 		}
750 		write("%\xc7\xec\x8f\xa2\n");
751 	}
752 
writeXrefAndTrailer()753 	void Writer::writeXrefAndTrailer()
754 	{
755 		QByteArray tmp;
756 		uint StX = bytesWritten();
757 		write("xref\n");
758 		write("0 "+Pdf::toPdf(m_ObjCounter)+"\n");
759 		//write("0000000000 65535 f \n");
760 		for (int a = 0; a < m_XRef.count(); ++a)
761 		{
762 			if (m_XRef[a] > 0)
763 			{
764 				tmp.setNum(m_XRef[a]);
765 				while (tmp.length()< 10)
766 				{
767 					tmp.prepend('0');
768 				}
769 				write(tmp);
770 				write(" 00000 n \n");
771 			}
772 			else
773 			{
774 				// unused object, mark as free-never-to-be-used-again
775 				write("0000000000 65535 f \n");
776 			}
777 		}
778 		write("trailer\n<<\n/Size "+Pdf::toPdf(m_XRef.count())+"\n");
779 		QByteArray IDs ="";
780 		for (uint cl = 0; cl < 16; ++cl)
781 			IDs += (m_FileID[cl]);
782 		QByteArray IDbytes = Pdf::toHexString(IDs);
783 		write("/Root 1 0 R\n/Info 2 0 R\n/ID ["+IDbytes+IDbytes+"]\n");
784 		if (EncryptObj > 0)
785 			write("/Encrypt "+Pdf::toObjRef(EncryptObj)+"\n");
786 		write(">>\nstartxref\n");
787 		write(Pdf::toPdf(StX)+"\n%%EOF\n");
788 	}
789 
write(const QByteArray & bytes)790 	void Writer::write(const QByteArray& bytes)
791 	{
792 		m_outStream.writeRawData(bytes, bytes.size());
793 	}
794 
write(const ResourceDictionary & dict)795 	void Writer::write(const ResourceDictionary& dict)
796 	{
797 		write("<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
798 		if (dict.XObject.count() != 0)
799 		{
800 			write("/XObject <<\n");
801 			ResourceMap::ConstIterator iti;
802 			for (iti = dict.XObject.begin(); iti != dict.XObject.end(); ++iti)
803 				write(Pdf::toName(iti.key()) + " " + Pdf::toObjRef(iti.value()) + "\n");
804 			write(">>\n");
805 		}
806 		if (dict.Font.count() != 0)
807 		{
808 			write("/Font << \n");
809 			QMap<QByteArray,PdfId>::ConstIterator it2;
810 			for (it2 = dict.Font.begin(); it2 != dict.Font.end(); ++it2)
811 				write(Pdf::toName(it2.key()) + " " + Pdf::toObjRef(it2.value()) + "\n");
812 			write(">>\n");
813 		}
814 		if (dict.Shading.count() != 0)
815 		{
816 			write("/Shading << \n");
817 			QMap<QByteArray,PdfId>::ConstIterator it3;
818 			for (it3 = dict.Shading.begin(); it3 != dict.Shading.end(); ++it3)
819 				write(Pdf::toName(it3.key()) + " " + Pdf::toObjRef(it3.value()) + "\n");
820 			write(">>\n");
821 		}
822 		if (dict.Pattern.count() != 0)
823 		{
824 			write("/Pattern << \n");
825 			QMap<QByteArray,PdfId>::ConstIterator it3p;
826 			for (it3p = dict.Pattern.begin(); it3p != dict.Pattern.end(); ++it3p)
827 				write(Pdf::toName(it3p.key()) + " " + Pdf::toObjRef(it3p.value()) + "\n");
828 			write(">>\n");
829 		}
830 		if (dict.ExtGState.count() != 0)
831 		{
832 			write("/ExtGState << \n");
833 			QMap<QByteArray,PdfId>::ConstIterator it3t;
834 			for (it3t = dict.ExtGState.begin(); it3t != dict.ExtGState.end(); ++it3t)
835 				write(Pdf::toName(it3t.key()) + " " + Pdf::toObjRef(it3t.value()) + "\n");
836 			write(">>\n");
837 		}
838 		if (dict.Properties.count() != 0)
839 		{
840 			write("/Properties << \n");
841 			QMap<QByteArray,PdfId>::ConstIterator it4p;
842 			for (it4p = dict.Properties.begin(); it4p != dict.Properties.end(); ++it4p)
843 				write(Pdf::toName(it4p.key()) + " " + Pdf::toObjRef(it4p.value()) + "\n");
844 			write(">>\n");
845 		}
846 		if (dict.ColorSpace.count() != 0)
847 		{
848 			write("/ColorSpace << \n");
849 			QList<Resource>::ConstIterator it3c;
850 			for (it3c = dict.ColorSpace.begin(); it3c != dict.ColorSpace.end(); ++it3c)
851 					write(Pdf::toName(it3c->ResName) + " " + Pdf::toObjRef(it3c->ResNum) + "\n");
852 
853 			write(">>\n");
854 		}
855 		write(">>\n");
856 	}
857 
reserveObjects(unsigned int n)858 	PdfId Writer::reserveObjects(unsigned int n)
859 	{
860 		assert( n < (1<<30) ); // should only be triggered by reserveObjects(-1) or similar
861 		PdfId result = m_ObjCounter;
862 		m_ObjCounter += n;
863 		return result;
864 	}
865 
startObj(PdfId id)866 	void Writer::startObj(PdfId id)
867 	{
868 		assert( m_CurrentObj == 0);
869 		m_CurrentObj = id;
870 		while (static_cast<uint>(m_XRef.length()) <= id)
871 			m_XRef.append(0);
872 		m_XRef[id] = bytesWritten();
873 		write(toPdf(id));
874 		write(" 0 obj\n");
875 	}
876 
endObj(PdfId id)877 	void Writer::endObj(PdfId id)
878 	{
879 		assert( m_CurrentObj == id);
880 		m_CurrentObj = 0;
881 		write("\nendobj\n");
882 	}
883 
endObjectWithStream(bool encrypted,PdfId id,const QByteArray & streamContent)884 	void Writer::endObjectWithStream(bool encrypted, PdfId id, const QByteArray& streamContent)
885 	{
886 		assert( m_CurrentObj == id);
887 		write("\nstream\n");
888 		write(encrypted? encryptBytes(streamContent, id): streamContent);
889 		write("\nendstream");
890 		endObj(id);
891 	}
892 
893 } // namespace PDF
894