1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #ifndef _FPDF_OBJECTS_ 8 #define _FPDF_OBJECTS_ 9 #ifndef _FXCRT_EXTENSION_ 10 #include "../fxcrt/fx_ext.h" 11 #endif 12 class CPDF_Document; 13 class CPDF_IndirectObjects; 14 class CPDF_Null; 15 class CPDF_Boolean; 16 class CPDF_Number; 17 class CPDF_String; 18 class CPDF_Stream; 19 class CPDF_StreamAcc; 20 class CPDF_StreamFilter; 21 class CPDF_Array; 22 class CPDF_Dictionary; 23 class CPDF_Reference; 24 class IPDF_DocParser; 25 class IFX_FileRead; 26 class CPDF_CryptoHandler; 27 #define PDFOBJ_INVALID 0 28 #define PDFOBJ_BOOLEAN 1 29 #define PDFOBJ_NUMBER 2 30 #define PDFOBJ_STRING 3 31 #define PDFOBJ_NAME 4 32 #define PDFOBJ_ARRAY 5 33 #define PDFOBJ_DICTIONARY 6 34 #define PDFOBJ_STREAM 7 35 #define PDFOBJ_NULL 8 36 #define PDFOBJ_REFERENCE 9 37 typedef IFX_FileStream* (*FPDF_LPFCloneStreamCallback)(CPDF_Stream *pStream, FX_LPVOID pUserData); 38 class CPDF_Object : public CFX_Object 39 { 40 public: 41 GetType()42 int GetType() const 43 { 44 return m_Type; 45 } 46 GetObjNum()47 FX_DWORD GetObjNum() const 48 { 49 return m_ObjNum; 50 } 51 52 FX_BOOL IsIdentical(CPDF_Object* pObj) const; 53 54 CPDF_Object* Clone(FX_BOOL bDirect = FALSE) const; 55 56 CPDF_Object* CloneRef(CPDF_IndirectObjects* pObjs) const; 57 58 CPDF_Object* GetDirect() const; 59 60 void Release(); 61 62 CFX_ByteString GetString() const; 63 64 CFX_ByteStringC GetConstString() const; 65 66 CFX_WideString GetUnicodeText(CFX_CharMap* pCharMap = NULL) const; 67 68 FX_FLOAT GetNumber() const; 69 70 FX_FLOAT GetNumber16() const; 71 72 int GetInteger() const; 73 74 CPDF_Dictionary* GetDict() const; 75 76 CPDF_Array* GetArray() const; 77 78 void SetString(const CFX_ByteString& str); 79 80 void SetUnicodeText(FX_LPCWSTR pUnicodes, int len = -1); 81 82 int GetDirectType() const; 83 IsModified()84 FX_BOOL IsModified() const 85 { 86 return FALSE; 87 } 88 protected: 89 FX_DWORD m_Type; CPDF_Object()90 CPDF_Object() 91 { 92 m_ObjNum = 0; 93 } 94 95 FX_DWORD m_ObjNum; 96 97 void Destroy(); 98 99 ~CPDF_Object()100 ~CPDF_Object() {} 101 friend class CPDF_IndirectObjects; 102 friend class CPDF_Parser; 103 friend class CPDF_SyntaxParser; 104 private: CPDF_Object(const CPDF_Object & src)105 CPDF_Object(const CPDF_Object& src) {} 106 CPDF_Object* CloneInternal(FX_BOOL bDirect, CFX_MapPtrToPtr* visited) const; 107 }; 108 class CPDF_Boolean : public CPDF_Object 109 { 110 public: 111 Create(FX_BOOL value)112 static CPDF_Boolean* Create(FX_BOOL value) 113 { 114 return FX_NEW CPDF_Boolean(value); 115 } 116 CPDF_Boolean()117 CPDF_Boolean() 118 { 119 m_Type = PDFOBJ_BOOLEAN; 120 } 121 CPDF_Boolean(FX_BOOL value)122 CPDF_Boolean(FX_BOOL value) 123 { 124 m_Type = PDFOBJ_BOOLEAN; 125 m_bValue = value; 126 } 127 Identical(CPDF_Boolean * pOther)128 FX_BOOL Identical(CPDF_Boolean* pOther) const 129 { 130 return m_bValue == pOther->m_bValue; 131 } 132 protected: 133 134 FX_BOOL m_bValue; 135 friend class CPDF_Object; 136 }; 137 class CPDF_Number : public CPDF_Object 138 { 139 public: 140 Create(int value)141 static CPDF_Number* Create(int value) 142 { 143 return FX_NEW CPDF_Number(value); 144 } 145 Create(FX_FLOAT value)146 static CPDF_Number* Create(FX_FLOAT value) 147 { 148 return FX_NEW CPDF_Number(value); 149 } 150 Create(FX_BSTR str)151 static CPDF_Number* Create(FX_BSTR str) 152 { 153 return FX_NEW CPDF_Number(str); 154 } 155 Create(FX_BOOL bInteger,void * pData)156 static CPDF_Number* Create(FX_BOOL bInteger, void* pData) 157 { 158 return FX_NEW CPDF_Number(bInteger, pData); 159 } 160 CPDF_Number()161 CPDF_Number() 162 { 163 m_Type = PDFOBJ_NUMBER; 164 } 165 166 CPDF_Number(FX_BOOL bInteger, void* pData); 167 168 CPDF_Number(int value); 169 170 CPDF_Number(FX_FLOAT value); 171 172 CPDF_Number(FX_BSTR str); 173 174 FX_BOOL Identical(CPDF_Number* pOther) const; 175 176 CFX_ByteString GetString() const; 177 178 void SetString(FX_BSTR str); 179 IsInteger()180 FX_BOOL IsInteger() const 181 { 182 return m_bInteger; 183 } 184 GetInteger()185 int GetInteger() const 186 { 187 return m_bInteger ? m_Integer : (int)m_Float; 188 } 189 GetNumber()190 FX_FLOAT GetNumber() const 191 { 192 return m_bInteger ? (FX_FLOAT)m_Integer : m_Float; 193 } 194 195 void SetNumber(FX_FLOAT value); 196 GetNumber16()197 FX_FLOAT GetNumber16() const 198 { 199 return GetNumber(); 200 } 201 GetFloat()202 FX_FLOAT GetFloat() const 203 { 204 return m_bInteger ? (FX_FLOAT)m_Integer : m_Float; 205 } 206 protected: 207 208 FX_BOOL m_bInteger; 209 210 union { 211 212 int m_Integer; 213 214 FX_FLOAT m_Float; 215 }; 216 friend class CPDF_Object; 217 }; 218 class CPDF_String : public CPDF_Object 219 { 220 public: 221 222 static CPDF_String* Create(const CFX_ByteString& str, FX_BOOL bHex = FALSE) 223 { 224 return FX_NEW CPDF_String(str, bHex); 225 } 226 Create(const CFX_WideString & str)227 static CPDF_String* Create(const CFX_WideString& str) 228 { 229 return FX_NEW CPDF_String(str); 230 } 231 CPDF_String()232 CPDF_String() 233 { 234 m_Type = PDFOBJ_STRING; 235 m_bHex = FALSE; 236 } 237 m_String(str)238 CPDF_String(const CFX_ByteString& str, FX_BOOL bHex = FALSE) : m_String(str) 239 { 240 m_Type = PDFOBJ_STRING; 241 m_bHex = bHex; 242 } 243 244 CPDF_String(const CFX_WideString& str); 245 GetString()246 CFX_ByteString& GetString() 247 { 248 return m_String; 249 } 250 Identical(CPDF_String * pOther)251 FX_BOOL Identical(CPDF_String* pOther) const 252 { 253 return m_String == pOther->m_String; 254 } 255 IsHex()256 FX_BOOL IsHex() const 257 { 258 return m_bHex; 259 } 260 protected: 261 262 CFX_ByteString m_String; 263 264 FX_BOOL m_bHex; 265 friend class CPDF_Object; 266 }; 267 class CPDF_Name : public CPDF_Object 268 { 269 public: 270 Create(const CFX_ByteString & str)271 static CPDF_Name* Create(const CFX_ByteString& str) 272 { 273 return FX_NEW CPDF_Name(str); 274 } 275 Create(FX_BSTR str)276 static CPDF_Name* Create(FX_BSTR str) 277 { 278 return FX_NEW CPDF_Name(str); 279 } 280 Create(FX_LPCSTR str)281 static CPDF_Name* Create(FX_LPCSTR str) 282 { 283 return FX_NEW CPDF_Name(str); 284 } 285 CPDF_Name(const CFX_ByteString & str)286 CPDF_Name(const CFX_ByteString& str) : m_Name(str) 287 { 288 m_Type = PDFOBJ_NAME; 289 } 290 CPDF_Name(FX_BSTR str)291 CPDF_Name(FX_BSTR str) : m_Name(str) 292 { 293 m_Type = PDFOBJ_NAME; 294 } 295 CPDF_Name(FX_LPCSTR str)296 CPDF_Name(FX_LPCSTR str) : m_Name(str) 297 { 298 m_Type = PDFOBJ_NAME; 299 } 300 GetString()301 CFX_ByteString& GetString() 302 { 303 return m_Name; 304 } 305 Identical(CPDF_Name * pOther)306 FX_BOOL Identical(CPDF_Name* pOther) const 307 { 308 return m_Name == pOther->m_Name; 309 } 310 protected: 311 312 CFX_ByteString m_Name; 313 friend class CPDF_Object; 314 }; 315 class CPDF_Array : public CPDF_Object 316 { 317 public: 318 Create()319 static CPDF_Array* Create() 320 { 321 return FX_NEW CPDF_Array(); 322 } 323 CPDF_Array()324 CPDF_Array() 325 { 326 m_Type = PDFOBJ_ARRAY; 327 } 328 GetCount()329 FX_DWORD GetCount() const 330 { 331 return m_Objects.GetSize(); 332 } 333 334 CPDF_Object* GetElement(FX_DWORD index) const; 335 336 CPDF_Object* GetElementValue(FX_DWORD index) const; 337 338 339 340 CFX_AffineMatrix GetMatrix(); 341 342 CFX_FloatRect GetRect(); 343 344 345 346 347 CFX_ByteString GetString(FX_DWORD index) const; 348 349 CFX_ByteStringC GetConstString(FX_DWORD index) const; 350 351 int GetInteger(FX_DWORD index) const; 352 353 FX_FLOAT GetNumber(FX_DWORD index) const; 354 355 CPDF_Dictionary* GetDict(FX_DWORD index) const; 356 357 CPDF_Stream* GetStream(FX_DWORD index) const; 358 359 CPDF_Array* GetArray(FX_DWORD index) const; 360 GetFloat(FX_DWORD index)361 FX_FLOAT GetFloat(FX_DWORD index) const 362 { 363 return GetNumber(index); 364 } 365 366 367 368 369 void SetAt(FX_DWORD index, CPDF_Object* pObj, CPDF_IndirectObjects* pObjs = NULL); 370 371 372 void InsertAt(FX_DWORD index, CPDF_Object* pObj, CPDF_IndirectObjects* pObjs = NULL); 373 374 void RemoveAt(FX_DWORD index); 375 376 377 void Add(CPDF_Object* pObj, CPDF_IndirectObjects* pObjs = NULL); 378 379 380 381 void AddNumber(FX_FLOAT f); 382 383 void AddInteger(int i); 384 385 void AddString(const CFX_ByteString& str); 386 387 void AddName(const CFX_ByteString& str); 388 389 void AddReference(CPDF_IndirectObjects* pDoc, FX_DWORD objnum); 390 AddReference(CPDF_IndirectObjects * pDoc,CPDF_Object * obj)391 void AddReference(CPDF_IndirectObjects* pDoc, CPDF_Object* obj) 392 { 393 AddReference(pDoc, obj->GetObjNum()); 394 } 395 396 GetNumber16(FX_DWORD index)397 FX_FLOAT GetNumber16(FX_DWORD index) const 398 { 399 return GetNumber(index); 400 } 401 AddNumber16(FX_FLOAT value)402 void AddNumber16(FX_FLOAT value) 403 { 404 AddNumber(value); 405 } 406 407 FX_BOOL Identical(CPDF_Array* pOther) const; 408 protected: 409 410 ~CPDF_Array(); 411 412 CFX_PtrArray m_Objects; 413 friend class CPDF_Object; 414 }; 415 class CPDF_Dictionary : public CPDF_Object 416 { 417 public: 418 Create()419 static CPDF_Dictionary* Create() 420 { 421 return FX_NEW CPDF_Dictionary(); 422 } 423 CPDF_Dictionary()424 CPDF_Dictionary() 425 { 426 m_Type = PDFOBJ_DICTIONARY; 427 } 428 429 430 431 CPDF_Object* GetElement(FX_BSTR key) const; 432 433 CPDF_Object* GetElementValue(FX_BSTR key) const; 434 435 436 437 438 439 CFX_ByteString GetString(FX_BSTR key) const; 440 441 CFX_ByteStringC GetConstString(FX_BSTR key) const; 442 443 CFX_ByteString GetString(FX_BSTR key, FX_BSTR default_str) const; 444 445 CFX_ByteStringC GetConstString(FX_BSTR key, FX_BSTR default_str) const; 446 447 CFX_WideString GetUnicodeText(FX_BSTR key, CFX_CharMap* pCharMap = NULL) const; 448 449 int GetInteger(FX_BSTR key) const; 450 451 int GetInteger(FX_BSTR key, int default_int) const; 452 453 FX_BOOL GetBoolean(FX_BSTR key, FX_BOOL bDefault = FALSE) const; 454 455 FX_FLOAT GetNumber(FX_BSTR key) const; 456 457 CPDF_Dictionary* GetDict(FX_BSTR key) const; 458 459 CPDF_Stream* GetStream(FX_BSTR key) const; 460 461 CPDF_Array* GetArray(FX_BSTR key) const; 462 463 CFX_FloatRect GetRect(FX_BSTR key) const; 464 465 CFX_AffineMatrix GetMatrix(FX_BSTR key) const; 466 GetFloat(FX_BSTR key)467 FX_FLOAT GetFloat(FX_BSTR key) const 468 { 469 return GetNumber(key); 470 } 471 472 473 FX_BOOL KeyExist(FX_BSTR key) const; 474 475 FX_POSITION GetStartPos() const; 476 477 CPDF_Object* GetNextElement(FX_POSITION& pos, CFX_ByteString& key) const; 478 479 void SetAt(FX_BSTR key, CPDF_Object* pObj, CPDF_IndirectObjects* pObjs = NULL); 480 481 482 483 void SetAtName(FX_BSTR key, const CFX_ByteString& name); 484 485 486 void SetAtString(FX_BSTR key, const CFX_ByteString& string); 487 488 489 void SetAtInteger(FX_BSTR key, int i); 490 491 492 void SetAtNumber(FX_BSTR key, FX_FLOAT f); 493 494 void SetAtReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, FX_DWORD objnum); 495 SetAtReference(FX_BSTR key,CPDF_IndirectObjects * pDoc,CPDF_Object * obj)496 void SetAtReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, CPDF_Object* obj) 497 { 498 SetAtReference(key, pDoc, obj->GetObjNum()); 499 } 500 501 void AddReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, FX_DWORD objnum); 502 AddReference(FX_BSTR key,CPDF_IndirectObjects * pDoc,CPDF_Object * obj)503 void AddReference(FX_BSTR key, CPDF_IndirectObjects* pDoc, CPDF_Object* obj) 504 { 505 AddReference(key, pDoc, obj->GetObjNum()); 506 } 507 508 void SetAtRect(FX_BSTR key, const CFX_FloatRect& rect); 509 510 void SetAtMatrix(FX_BSTR key, const CFX_AffineMatrix& matrix); 511 512 void SetAtBoolean(FX_BSTR key, FX_BOOL bValue); 513 514 515 516 void RemoveAt(FX_BSTR key); 517 518 519 void ReplaceKey(FX_BSTR oldkey, FX_BSTR newkey); 520 521 FX_BOOL Identical(CPDF_Dictionary* pDict) const; 522 GetCount()523 int GetCount() const 524 { 525 return m_Map.GetCount(); 526 } 527 528 void AddValue(FX_BSTR key, CPDF_Object* pObj); 529 protected: 530 531 ~CPDF_Dictionary(); 532 533 CFX_CMapByteStringToPtr m_Map; 534 535 friend class CPDF_Object; 536 }; 537 class CPDF_Stream : public CPDF_Object 538 { 539 public: 540 Create(FX_LPBYTE pData,FX_DWORD size,CPDF_Dictionary * pDict)541 static CPDF_Stream* Create(FX_LPBYTE pData, FX_DWORD size, CPDF_Dictionary* pDict) 542 { 543 return FX_NEW CPDF_Stream(pData, size, pDict); 544 } 545 546 CPDF_Stream(FX_LPBYTE pData, FX_DWORD size, CPDF_Dictionary* pDict); 547 GetDict()548 CPDF_Dictionary* GetDict() const 549 { 550 return m_pDict; 551 } 552 553 void SetData(FX_LPCBYTE pData, FX_DWORD size, FX_BOOL bCompressed, FX_BOOL bKeepBuf); 554 555 void InitStream(FX_BYTE* pData, FX_DWORD size, CPDF_Dictionary* pDict); 556 557 void InitStream(IFX_FileRead *pFile, CPDF_Dictionary* pDict); 558 559 FX_BOOL Identical(CPDF_Stream* pOther) const; 560 561 CPDF_StreamFilter* GetStreamFilter(FX_BOOL bRaw = FALSE) const; 562 563 564 GetRawSize()565 FX_DWORD GetRawSize() const 566 { 567 return m_dwSize; 568 } 569 570 FX_BOOL ReadRawData(FX_FILESIZE start_pos, FX_LPBYTE pBuf, FX_DWORD buf_size) const; 571 572 IsMemoryBased()573 FX_BOOL IsMemoryBased() const 574 { 575 return m_GenNum == (FX_DWORD) - 1; 576 } 577 578 CPDF_Stream* Clone(FX_BOOL bDirect, FPDF_LPFCloneStreamCallback lpfCallback, FX_LPVOID pUserData) const; 579 protected: 580 581 ~CPDF_Stream(); 582 583 CPDF_Dictionary* m_pDict; 584 585 FX_DWORD m_dwSize; 586 587 FX_DWORD m_GenNum; 588 589 union { 590 591 FX_LPBYTE m_pDataBuf; 592 593 IFX_FileRead* m_pFile; 594 }; 595 596 FX_FILESIZE m_FileOffset; 597 598 CPDF_CryptoHandler* m_pCryptoHandler; 599 600 void InitStream(CPDF_Dictionary* pDict); 601 friend class CPDF_Object; 602 friend class CPDF_StreamAcc; 603 friend class CPDF_AttachmentAcc; 604 }; 605 class CPDF_StreamAcc : public CFX_Object 606 { 607 public: 608 609 CPDF_StreamAcc(); 610 611 ~CPDF_StreamAcc(); 612 613 void LoadAllData(const CPDF_Stream* pStream, FX_BOOL bRawAccess = FALSE, 614 FX_DWORD estimated_size = 0, FX_BOOL bImageAcc = FALSE); 615 GetStream()616 const CPDF_Stream* GetStream() const 617 { 618 return m_pStream; 619 } 620 GetDict()621 CPDF_Dictionary* GetDict() const 622 { 623 return m_pStream->GetDict(); 624 } 625 626 FX_LPCBYTE GetData() const; 627 628 FX_DWORD GetSize() const; 629 630 FX_LPBYTE DetachData(); 631 GetImageDecoder()632 const CFX_ByteString& GetImageDecoder() 633 { 634 return m_ImageDecoder; 635 } 636 GetImageParam()637 const CPDF_Dictionary* GetImageParam() 638 { 639 return m_pImageParam; 640 } 641 protected: 642 643 FX_LPBYTE m_pData; 644 645 FX_DWORD m_dwSize; 646 647 FX_BOOL m_bNewBuf; 648 649 CFX_ByteString m_ImageDecoder; 650 651 CPDF_Dictionary* m_pImageParam; 652 653 const CPDF_Stream* m_pStream; 654 655 FX_LPBYTE m_pSrcData; 656 }; 657 CFX_DataFilter* FPDF_CreateFilter(FX_BSTR name, const CPDF_Dictionary* pParam, int width = 0, int height = 0); 658 #define FPDF_FILTER_BUFFER_SIZE 20480 659 class CPDF_StreamFilter : public CFX_Object 660 { 661 public: 662 663 ~CPDF_StreamFilter(); 664 665 FX_DWORD ReadBlock(FX_LPBYTE buffer, FX_DWORD size); 666 GetSrcPos()667 FX_DWORD GetSrcPos() 668 { 669 return m_SrcOffset; 670 } 671 GetStream()672 const CPDF_Stream* GetStream() 673 { 674 return m_pStream; 675 } 676 protected: 677 CPDF_StreamFilter()678 CPDF_StreamFilter() {} 679 680 FX_DWORD ReadLeftOver(FX_LPBYTE buffer, FX_DWORD buf_size); 681 682 const CPDF_Stream* m_pStream; 683 684 CFX_DataFilter* m_pFilter; 685 686 CFX_BinaryBuf* m_pBuffer; 687 688 FX_DWORD m_BufOffset; 689 690 FX_DWORD m_SrcOffset; 691 692 FX_BYTE m_SrcBuffer[FPDF_FILTER_BUFFER_SIZE]; 693 friend class CPDF_Stream; 694 }; 695 class CPDF_Null : public CPDF_Object 696 { 697 public: 698 Create()699 static CPDF_Null* Create() 700 { 701 return FX_NEW CPDF_Null(); 702 } 703 CPDF_Null()704 CPDF_Null() 705 { 706 m_Type = PDFOBJ_NULL; 707 } 708 }; 709 class CPDF_Reference : public CPDF_Object 710 { 711 public: 712 Create(CPDF_IndirectObjects * pDoc,int objnum)713 static CPDF_Reference* Create(CPDF_IndirectObjects* pDoc, int objnum) 714 { 715 return FX_NEW CPDF_Reference(pDoc, objnum); 716 } 717 CPDF_Reference(CPDF_IndirectObjects * pDoc,int objnum)718 CPDF_Reference(CPDF_IndirectObjects* pDoc, int objnum) 719 { 720 m_Type = PDFOBJ_REFERENCE; 721 m_pObjList = pDoc; 722 m_RefObjNum = objnum; 723 } 724 GetObjList()725 CPDF_IndirectObjects* GetObjList() const 726 { 727 return m_pObjList; 728 } 729 GetRefObjNum()730 FX_DWORD GetRefObjNum() const 731 { 732 return m_RefObjNum; 733 } 734 735 void SetRef(CPDF_IndirectObjects* pDoc, FX_DWORD objnum); 736 Identical(CPDF_Reference * pOther)737 FX_BOOL Identical(CPDF_Reference* pOther) const 738 { 739 return m_RefObjNum == pOther->m_RefObjNum; 740 } 741 protected: 742 743 CPDF_IndirectObjects* m_pObjList; 744 745 FX_DWORD m_RefObjNum; 746 friend class CPDF_Object; 747 }; 748 class CPDF_IndirectObjects : public CFX_Object 749 { 750 public: 751 752 CPDF_IndirectObjects(IPDF_DocParser* pParser); 753 754 ~CPDF_IndirectObjects(); 755 756 CPDF_Object* GetIndirectObject(FX_DWORD objnum, struct PARSE_CONTEXT* pContext = NULL); 757 758 int GetIndirectType(FX_DWORD objnum); 759 760 FX_DWORD AddIndirectObject(CPDF_Object* pObj); 761 762 void ReleaseIndirectObject(FX_DWORD objnum); 763 764 void InsertIndirectObject(FX_DWORD objnum, CPDF_Object* pObj); 765 766 FX_DWORD GetLastObjNum() const; 767 GetStartPosition()768 FX_POSITION GetStartPosition() const 769 { 770 return m_IndirectObjs.GetStartPosition(); 771 } 772 GetNextAssoc(FX_POSITION & rPos,FX_DWORD & objnum,CPDF_Object * & pObject)773 void GetNextAssoc(FX_POSITION& rPos, FX_DWORD& objnum, CPDF_Object*& pObject) const 774 { 775 m_IndirectObjs.GetNextAssoc(rPos, (void*&)objnum, (void*&)pObject); 776 } 777 protected: 778 779 CFX_MapPtrToPtr m_IndirectObjs; 780 781 IPDF_DocParser* m_pParser; 782 783 FX_DWORD m_LastObjNum; 784 }; 785 #endif 786