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