/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// // OdOleSsItemHandlerImpl.cpp: implementation of the OdOleSsItemHandler class. // ////////////////////////////////////////////////////////////////////// #if defined(OLESS_WIN_GDI_SUPPORT) #include // MFC core and standard components #include // MFC extensions #include // MFC support for Internet Explorer 4 Common Controls #endif #include "OdaCommon.h" #include "Gi/GiCommonDraw.h" #include "Gi/GiDrawableImpl.h" #include "Gi/GiViewportDraw.h" #include "Gs/Gs.h" #include "OleSsItemHandlerModule.h" #include "OdPlatformStreamer.h" #include "FlatMemStream.h" #include "MemoryStream.h" #include "DynamicLinker.h" #include "StaticRxObject.h" #include "RxRasterServices.h" #include "OdSSRWIS.h" #include "Gi/GiMetafile.h" #include "Gs/GsBaseVectorizer.h" #include "OleSsItemHandlerImpl.h" #include "CompDocData.h" #if defined(OLESS_WIN_GDI_SUPPORT) && defined(ODA_WINDOWS) #include "Gi/GiRasterWrappers.h" #endif #include "OdCharMapper.h" //#ifdef USE_METAFILE_SUPPORT #include "MetafileSupport.h" #include "RxVariantValue.h" //#endif #ifdef _MSC_VER OD_DISABLE_WARNING(4100) #endif //#if defined(_DEBUG) && defined(ODA_WINDOWS) && !defined(_WINRT) //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/core-26849 Incorrect type for OdDbOle2Frame with using OdOleSsItemHandler in attached files" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/dgn_ole" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/forum-23423-Require-Information-about-Displaying-Ole-Entity" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/core-20462 forum_link_path_with_OleSs/1" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-20308 forum_23076-Incorrect-type-for-OdDbOle2Frame/linked" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-19797 OdGiRasterImage scanLines() method returns incorrect value/save" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-19521 Exception while reading OLE in attached file with using OleSsItemHandler/save" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-17059 Reading problem in embedded OLE images in DWG file/save" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-17010 OleSsItemHandler can not load OLE entity/save" //#define PATH_TO_SAVE_INTERNAL_DATA L"e:/_Oda/data/_ole/-core-13785 Can not get OLE" //#endif #ifdef PATH_TO_SAVE_INTERNAL_DATA #include "../Kernel/Extensions/ExRender/TrXml/IO/CloudTools.h" static void saveInternalData(const OdBinaryData& data, const OdString& sPref, const OdString& sSfx) { if (!OdFilePathName(PATH_TO_SAVE_INTERNAL_DATA).existsDir()) return; OdString path; for (int idx = 0; OdFilePathName(path.format(L"%ls/%ls%04d%ls", PATH_TO_SAVE_INTERNAL_DATA, sPref.c_str(), idx, sSfx.c_str())).exists();) idx++; OdStreamBufPtr io = ::odrxSystemServices()->createFile(path, Oda::kFileWrite, Oda::kShareDenyNo, Oda::kCreateAlways); if (io.get()) io->putBytes(data.asArrayPtr(), data.size()); } #else # define saveInternalData(data, sPref, sSfx) #endif //#define DROP_PRES_STREAM ODRX_NO_CONS_DEFINE_MEMBERS(OdOleSsItemHandlerImpl, OdOleItemHandlerBase); ODRX_NO_CONS_DEFINE_MEMBERS(OdGiSelfGiDrawablePEImpl, OdGiSelfGiDrawablePE); class OdOleSsCompObj { public: // It stores information on Application type including 3 strings and some unknown data probably also some flags - settings. //16 bytes – unknown. First 12 are constant. OdUInt8 m_unknown12[12]; CLSID m_clsId; //Offset 16 – 4 bytes – first string length including Null terminator //First string (ASCIIZ) OdAnsiString m_asFirst; //4 bytes – second string length //Second string (ASCIIZ) OdUInt32 m_lenSecond; OdUInt8 m_unknown4[4]; // is used if m_lenSecond == 0xffffffff OdAnsiString m_asSecond; //4 bytes – third string length OdAnsiString m_asThird; //Third string (ASCIIZ) //16 bytes unknown (some flags/settings) – constant OdUInt8 m_unknown16[16]; OdOleSsCompObj() { static const OdUInt8 content[] = OD_COMP_DOC_COMP_OBJ_DATA; const OdUInt8* pCtx = content; ::memcpy(m_unknown12, pCtx, sizeof(m_unknown12)); pCtx += sizeof(m_unknown12); ::memcpy(&m_clsId, pCtx, sizeof(m_clsId)); pCtx += sizeof(m_clsId); m_asFirst = m_asSecond = m_asThird = "PBrush"; m_lenSecond = m_asSecond.getLength() + 1; pCtx += 3 * (sizeof(OdUInt32) + m_lenSecond); ::memcpy(m_unknown16, pCtx, sizeof(m_unknown16)); //pCtx += sizeof(m_unknown16); } virtual ~OdOleSsCompObj() { } void inFileds(OdStreamBufPtr pStream) { pStream->getBytes(m_unknown12, sizeof(m_unknown12)); pStream->getBytes(&m_clsId, sizeof(m_clsId)); OdUInt32 lenFirst = OdPlatformStreamer::rdInt32(*pStream); pStream->getBytes(m_asFirst.getBuffer(lenFirst), lenFirst); m_asFirst.releaseBuffer(); m_lenSecond = OdPlatformStreamer::rdInt32(*pStream); if (m_lenSecond != 0xffffffff) pStream->getBytes(m_asSecond.getBuffer(m_lenSecond), m_lenSecond), m_asSecond.releaseBuffer(); else { pStream->getBytes(m_unknown4, sizeof(m_unknown4)); ODA_ASSERT_ONCE(m_unknown4[0] != 0 && !m_unknown4[1] && !m_unknown4[2] && !m_unknown4[3]); } OdUInt32 lenThird = OdPlatformStreamer::rdInt32(*pStream); pStream->getBytes(m_asThird.getBuffer(lenThird), lenThird); m_asThird.releaseBuffer(); pStream->getBytes(m_unknown16, sizeof(m_unknown16)); } void outFileds(OdStreamBufPtr pStream) { pStream->putBytes(m_unknown12, sizeof(m_unknown12)); pStream->putBytes(&m_clsId, sizeof(m_clsId)); OdUInt32 lenFirst = m_asFirst.getLength() + 1; OdPlatformStreamer::wrInt32(*pStream, lenFirst); pStream->putBytes(m_asFirst.c_str(), lenFirst); OdPlatformStreamer::wrInt32(*pStream, m_lenSecond); if (m_lenSecond != 0xffffffff) { ODA_ASSERT_ONCE(m_lenSecond == OdUInt32(m_asSecond.getLength() + 1)); pStream->putBytes(m_asSecond.c_str(), m_lenSecond); } else { ODA_ASSERT_ONCE(m_unknown4[0] != 0 && !m_unknown4[1] && !m_unknown4[2] && !m_unknown4[3]); pStream->putBytes(m_unknown4, sizeof(m_unknown4)); } OdUInt32 lenThird = m_asThird.getLength() + 1; OdPlatformStreamer::wrInt32(*pStream, lenThird); pStream->putBytes(m_asThird.c_str(), lenThird); pStream->putBytes(m_unknown16, sizeof(m_unknown16)); } }; class OdOleSsOlePaths { public: OdUInt8 m_unknown0[16]; // ? magic 01 00 00 02 01 00 00 00 01 00 OdStringArray m_asLinkedPaths; // the last (== via utf16) is link path. others are parts of link name OdIntArray m_ids; OdOleSsOlePaths() { ::memset(m_unknown0, 0, sizeof(m_unknown0)); } virtual ~OdOleSsOlePaths() { } void inFileds(OdStreamBufPtr pStream) { OdUInt64 sz = pStream->length(); if (sz <= sizeof(m_unknown0)) return; int idx = 0, idxSectionEnd = 0; // none try { pStream->getBytes(m_unknown0, sizeof(m_unknown0)); // 16 idx += sizeof(m_unknown0); enum { enState_Init, enState_Id, //enState_Mark_C0, enState_Mark_46, enState_TextLen, //enState_AsciiLen, enState_UTF16Len, enState_Ascii, enState_UTF16 } state = enState_TextLen; // enState_Init; int idxRec = 0, numRec = 1000, // initial id0 = 0, id1 = 0; bool bSkip = false; OdUInt8 dataRec[64]; OdUInt8* pData = dataRec; int lenDataRec = 4, lenAscii = 0, lenUtf16 = 0; for (; idxRec < numRec; idxRec++, pData += lenDataRec) { if (&pData[4] >= &dataRec[64]) pData = dataRec; //lenDataRec = 4; if ((idx + lenDataRec) > sz) return; pStream->getBytes(pData, lenDataRec); idx += lenDataRec; //ODA_ASSERT_VAR(if (idx >= 0x66)) // ODA_ASSERT(true); // brk switch (state) { case enState_TextLen: // or enState_Mark_C0 if (!pData[0] && !pData[1] && !pData[2] && !pData[3]) // 0 0 0 0 continue; // start.png ... if ((pData[0] || pData[1]) && pData[2] == 0xAD && pData[3] == 0xDE) // . . AD DE { state = enState_Id; id0 = id1 = 0; bSkip = false; continue; } if (pData[0] == 0xC0 && !pData[1] && !pData[2] && !pData[3]) // C0 0 0 0 { state = enState_Mark_46; continue; } if ((pData[0] == 3 || !pData[0]) && !pData[1] && pData[2] && !pData[3]) { bSkip = !pData[0]; pData[0] = pData[2]; pData[1] = pData[3]; pData[2] = OdPlatformStreamer::rdInt8(*pStream), idx++; pData[3] = OdPlatformStreamer::rdInt8(*pStream), idx++; } if ( pData[0] // && !pData[1] && !pData[2] && !pData[3]) { if (idxSectionEnd && (idx - lenDataRec) == idxSectionEnd && (id0 || id1)) id0 = id1 = idxSectionEnd = 0, bSkip = false; if (!id0 && !id1) // by enState_Init { if (!idxSectionEnd) { ////ODA_ASSERT_ONCE((idx - lenDataRec) == sizeof(m_unknown0)); idxSectionEnd = pData[0] + (pData[1] << 8) + (idx - lenDataRec); } state = enState_Id; continue; } } if (pData[0] && !pData[1] && !pData[2] && !pData[3]) { ODA_ASSERT_ONCE(pData[0] > 1); if (pData[0] == 1) return; lenAscii = pData[0]; state = enState_Ascii; break; } break; case enState_Id: ODA_ASSERT_ONCE(!id0 && !id1); if (!pData[0] && !pData[1] && !pData[2] && !pData[3]) // 0 0 0 0 continue; if (pData[0] && pData[1] && !pData[2] && !pData[3]) { id0 = pData[0]; id1 = pData[1]; if (m_ids.size() > m_asLinkedPaths.size()) m_ids[m_ids.size() - 1] = id1; else m_ids.push_back(id1); state = enState_TextLen; continue; } ODA_ASSERT_ONCE(!id0 && !id1); // TODO break; case enState_Mark_46: if (!pData[0] && !pData[1] && !pData[2] && pData[3]) // 0 0 0 0x46 { state = enState_TextLen; continue; } ODA_FAIL_ONCE(); // TODO break; } if (state != enState_Ascii || lenAscii < 2) continue; if (lenAscii) { ODA_ASSERT_ONCE(!lenUtf16); OdAnsiString sLinkedPath; char* pBuf = sLinkedPath.getBuffer(lenAscii+2); pBuf[0] = OdPlatformStreamer::rdInt8(*pStream), idx++; pBuf[1] = OdPlatformStreamer::rdInt8(*pStream), idx++; if (pBuf[0] == 3 && pBuf[1] == 3) { pBuf[2] = OdPlatformStreamer::rdInt8(*pStream), idx++; pBuf[3] = OdPlatformStreamer::rdInt8(*pStream), idx++; ODA_ASSERT_ONCE(!pBuf[2] && !pBuf[3]); //case enState_Id: id0 = pBuf[0]; id1 = pBuf[1]; bSkip = false; if (m_ids.size() > m_asLinkedPaths.size()) { ODA_ASSERT_ONCE(m_ids.last() == id1); m_ids[m_ids.size() - 1] = id1; } else { ODA_FAIL_ONCE(); m_ids.push_back(id1); } state = enState_TextLen; continue; } else if (pBuf[0] == 3 && !pBuf[1]) { state = enState_UTF16; lenUtf16 = lenAscii; lenAscii = 0; } else { pStream->getBytes(&pBuf[2], lenAscii - 2); pBuf[lenAscii] = '\0'; idx += lenAscii - 2; sLinkedPath.releaseBuffer(); if (!bSkip || !m_asLinkedPaths.size()) { if (m_asLinkedPaths.size() == m_ids.size()) m_asLinkedPaths[m_asLinkedPaths.size() - 1] = m_asLinkedPaths.last() + sLinkedPath.c_str(); else m_asLinkedPaths.push_back(sLinkedPath); } ODA_ASSERT_VAR(else) ODA_ASSERT(true); // brk } } if (lenUtf16) { ODA_ASSERT_ONCE(!lenAscii); OdString sLinkedPath; int idxStart = idx; OdChar* pBuf = sLinkedPath.getBuffer(lenUtf16 + 1); for (; idx < (idxStart + lenUtf16 - 1); idx += 2) *pBuf++ = (OdChar)OdPlatformStreamer::rdInt16(*pStream); *pBuf++ = (OdChar)0; sLinkedPath.releaseBuffer(); m_asLinkedPaths.push_back(sLinkedPath); return; // it looks like next data are corrupted and useless } state = enState_TextLen; lenUtf16 = lenAscii = 0; } // for (; idxRec < numRec; idxRec++) } catch (OdError& ODA_ASSERT_VAR(e)) // eEndOfFile { ODA_ASSERT_ONCE(e.code() == eEndOfFile); } } }; class OdOleSsNative { public: //First 4 bytes – stream data length //In case of PBrush application bitmap starts here. //In other cases probably format may be different but for Package (JPG) it is: //Int32 bytes – stream length //Int16 – unknown – equal to “2” //Null terminated label – “WriteEx.jpg” OdAnsiString m_asLabel0; //Some string null terminated - “C:\WINDOWS\system32\SHELL32.dll” OdAnsiString m_asLabel1; //Int32 - unknown OdUInt32 m_unknown32; //Int32 – length of following string including Null terminator OdUInt32 m_lenSource; OdAnsiString m_asSource; //Source file name string null terminated – “D:\ODA\#CURTR~1\exe\VC8DLL~1/ODWRIT~1.JPG” //Int32 – Length of data //Data follows - .JPG file contents. bool m_isNativePBrush; OdBinaryData m_data; int m_lenUnknown10; // at end OdUInt8 m_unknown10[10]; // at end OdOleSsNative(bool isNativePBrush = true) : m_isNativePBrush(isNativePBrush) { m_lenUnknown10 = 10; ::memset(m_unknown10, 0, sizeof(m_unknown10)); } OdOleSsNative(OdGiRasterImagePtr pImage) : m_isNativePBrush(true) { ODA_ASSERT_ONCE(pImage.get()); OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); OdMemoryStreamPtr pStreamBuf = OdMemoryStream::createNew(); if (pRasSvcs.isNull() || !pRasSvcs->convertRasterImage(pImage, OdRxRasterServices::kBMP, pStreamBuf)) { ODA_FAIL_ONCE(); return; } pStreamBuf->rewind(); OdUInt32 nSize = (OdUInt32) pStreamBuf->length(); ODA_ASSERT_ONCE(nSize); m_data.resize(nSize); if (nSize) pStreamBuf->getBytes(m_data.asArrayPtr(), m_data.size()); m_lenUnknown10 = 10; ::memset(m_unknown10, 0, sizeof(m_unknown10)); } virtual ~OdOleSsNative() { } void inFileds(OdStreamBufPtr pStream) { OdUInt64 lenStr = pStream->length(); (void)lenStr; OdInt32 len = OdPlatformStreamer::rdInt32(*pStream); if (!m_isNativePBrush) { OdInt16 inknown0 = OdPlatformStreamer::rdInt16(*pStream); ODA_ASSERT_ONCE(inknown0 == 2); char* pBuf = m_asLabel0.getBuffer(1024); while ((*pBuf = (char) pStream->getByte()) != 0) pBuf++; m_asLabel0.releaseBuffer(); ODA_ASSERT_ONCE(m_asLabel0.getLength() < 1024); pBuf = m_asLabel1.getBuffer(1024); while ((*pBuf = (char) pStream->getByte()) != 0) pBuf++; m_asLabel1.releaseBuffer(); ODA_ASSERT_ONCE(m_asLabel1.getLength() < 1024); m_unknown32 = OdPlatformStreamer::rdInt32(*pStream); ODA_ASSERT_ONCE(m_unknown32 == 0x30000); m_lenSource = OdPlatformStreamer::rdInt32(*pStream); pStream->getBytes(m_asSource.getBuffer(m_lenSource), m_lenSource); m_asSource.releaseBuffer(); //ODA_ASSERT_ONCE(pStream->tell() >= 0x66); // ole.dwg len = OdPlatformStreamer::rdInt32(*pStream); } if (m_isNativePBrush) { ODA_ASSERT_ONCE(m_lenUnknown10 == 2 || m_lenUnknown10 == 10); m_data.resize(len - m_lenUnknown10); // -sizeof(m_unknown10)); } else m_data.resize(len); pStream->getBytes(m_data.asArrayPtr(), m_data.size()); if (m_isNativePBrush) { ODA_ASSERT_ONCE(m_lenUnknown10 == 2 || m_lenUnknown10 == 10); pStream->getBytes(m_unknown10, m_lenUnknown10); // sizeof(m_unknown10)); } } void outFileds(OdStreamBufPtr pStream) { ODA_ASSERT_ONCE(m_isNativePBrush && m_data.size()); OdInt32 len = m_data.size(); if (m_isNativePBrush) { ODA_ASSERT_ONCE(m_lenUnknown10 == 2 || m_lenUnknown10 == 10); len += m_lenUnknown10; // += sizeof(m_unknown10); } OdPlatformStreamer::wrInt32(*pStream, len); if (len) pStream->putBytes(m_data.asArrayPtr(), m_data.size()); if (m_isNativePBrush) { ODA_ASSERT_ONCE(m_lenUnknown10 == 2 || m_lenUnknown10 == 10); pStream->putBytes(m_unknown10, m_lenUnknown10); // sizeof(m_unknown10)); } } }; class OdOleSsOle { public: OdUInt8 m_unknown20[20]; OdOleSsOle() { static const OdUInt8 content[] = OD_COMP_DOC_OLE_DATA; ::memcpy(m_unknown20, content, sizeof(content)); } virtual ~OdOleSsOle() { } void inFileds(OdStreamBufPtr pStream) { pStream->getBytes(m_unknown20, 20); } void outFileds(OdStreamBufPtr pStream) { pStream->putBytes(m_unknown20, 20); } }; OdOleSsItemHandlerImpl::OdOleSsItemHandlerImpl() : OdOleItemHandlerBase() //# ifdef USE_METAFILE_SUPPORT , m_bImageCacheChecked(false) //# endif , m_type(kUnknown) // to take via ole2frame { //char* pLeakMarker = (char *)odrxAlloc(200); //memset(pLeakMarker, 0, 200); //strcpy(pLeakMarker, "Marker_create"); // since this object is not DB object and can't be turned to proxy, // we should lock its module to prevent unloading. OdOleSsItemHandlerModuleImpl::internalLockModule(); //#ifdef USE_METAFILE_SUPPORT if (OdRxDictionary* pProperties = OdRxDictionary::cast(OdOleSsItemHandlerModuleImpl::instance()).get()) { if (pProperties->has(L"WmfSkipGetRaster")) { bool bWmfSkipGetRaster = OdRxVariantValue(pProperties->getAt(L"WmfSkipGetRaster").get())->getBool(); if (bWmfSkipGetRaster) m_bImageCacheChecked = true; } } //#endif } OdOleSsItemHandlerImpl::~OdOleSsItemHandlerImpl() { OdOleSsItemHandlerModuleImpl::internalUnlockModule(); // unlock module //char* pLeakMarker = (char *)odrxAlloc(200); //memset(pLeakMarker, 0, 200); //strcpy(pLeakMarker, "Marker_destroy"); } static bool isWmfByContent(const OdBinaryData& dataImage) { if (dataImage.size() <= 4) return false; for (int idx = 0; idx < 4; idx++) { OdUInt8 bt = dataImage.getAt(idx); if ( (!idx && bt == 0x01) || (idx == 1 && !bt) || (idx == 2 && bt == 0x09) || (idx == 3 && !bt)) continue; return false; } return true; } static bool isEmfByContent(const OdBinaryData& dataImage) { if (dataImage.size() <= 4) return false; for (int idx = 0; idx < 4; idx++) { OdUInt8 bt = dataImage.getAt(idx); if ((!idx && bt == 0x01) || (idx && !bt)) continue; return false; } return true; } static OdString getExtByContent(const OdBinaryData& dataImage) { OdString sExt; if (!dataImage.size()) return sExt; OdUInt8 frst = dataImage.getAt(0); switch (frst) { case 1: sExt = isEmfByContent(dataImage) ? L".emf" : L".wmf"; break; case 0x42: sExt = L".bmp"; break; case 0x47: sExt = L".gif"; break; case 0x49: sExt = L".tif"; break; case 0x89: sExt = L".png"; break; case 0xFF: sExt = L".jpg"; break; default: sExt = L".unknown"; break; } saveInternalData(dataImage, L"preview_", sExt); // PATH_TO_SAVE_INTERNAL_DATA return sExt; } void OdOleSsItemHandlerImpl::setCompoundDocument(OdUInt32 nSize, OdStreamBuf& stream) { if (nSize == 0) { m_pImage = NULL; m_dataMetafile.clear(); } else { m_buff.resize(nSize); stream.getBytes(m_buff.asArrayPtr(), nSize); saveInternalData(m_buff, L"storage.", L""); // PATH_TO_SAVE_INTERNAL_DATA #ifndef DROP_PRES_STREAM OdStreamBufPtr pMemStream = OdFlatMemStream::createNew(m_buff.asArrayPtr(), m_buff.size()); struct _RootStorage* pRootStorage = openFileStorage(pMemStream.get()); #else OdStreamBufPtr pMemStream = OdMemoryStream::createNew(); pMemStream->putBytes(m_buff.asArrayPtr(), m_buff.size()); pMemStream->rewind(); struct _RootStorage* pRootStorage = openFileStorage(pMemStream.get(), STG_RW); #endif OdBinaryData dataImageNative, dataImagePress, dataImageContent, dataPackage; Storage* pStorage = NULL; OdOleSsOlePaths olePaths; if (pRootStorage && (pStorage = getStorage(pRootStorage)) != NULL) { Stream* pStrm = NULL; unsigned long nOffsetInContents = 22; // for wmf bool isNativePBrush = true; OdOleSsCompObj compObj; OdOleSsNative native; if ((pStrm = openStorageStream(pStorage, L"\01CompObj")) != NULL) { OdBinaryData data = readStorageStream(&pStrm); OdStreamBufPtr pStream = OdFlatMemStream::createNew(data.asArrayPtr(), data.size()); compObj.inFileds(pStream); if (compObj.m_asThird == "OLE Package" || compObj.m_asThird == "Package") { ODA_ASSERT_ONCE(compObj.m_asFirst == "OLE Package" || compObj.m_asFirst == "Package"); // && compObj.m_asSecond.isEmpty()); isNativePBrush = false; m_type = kEmbedded; } if (compObj.m_asThird == "PBrush") m_sUserType = L"Paintbrush Picture"; else if (compObj.m_asThird.find("Static") >= 0) m_type = kStatic; if (compObj.m_asThird == "StaticDib") // compObj.m_asFirst == "Picture (Device Independent Bitmap)" nOffsetInContents = 0; if (compObj.m_asThird == "StaticEnhancedMetafile") nOffsetInContents = 0x70; // EMF - DGN way for https://forum.opendesign.com/showthread.php?23430-OLE-Embedding-on-Linux-in-DGN-gt-PDF-Export-is-not-Rendering if (!compObj.m_asFirst.isEmpty()) { // CORE-15682 //OdCharArray buf; //OdCharMapper::utf8ToUnicode(compObj.m_asFirst.c_str(), compObj.m_asFirst.getLength(), buf); //m_sUserType = buf.getPtr(); // strange but it does not work m_sUserType = compObj.m_asFirst; int pos = m_sUserType.trimLeft().trimRight().reverseFind(' '), pos0 = m_sUserType.find(' '); if (pos > 0 && pos > pos0) m_sUserType = compObj.m_asFirst.left(pos); } } if ((pStrm = openStorageStream(pStorage, L"\01Ole")) != NULL) // ole_past_bmp.dwg { OdBinaryData data = readStorageStream(&pStrm); OdStreamBufPtr pStream = OdFlatMemStream::createNew(data.asArrayPtr(), data.size()); olePaths.inFileds(pStream); } if ((pStrm = openStorageStream(pStorage, L"\01Ole10Native")) != NULL) // ole_past_bmp.dwg { native.m_isNativePBrush = isNativePBrush; if (m_type == kUnknown) // problem with OLE.dwg CORE-20308 m_type = isNativePBrush ? kEmbedded : kLink; OdBinaryData data = readStorageStream(&pStrm); OdStreamBufPtr pStream = OdFlatMemStream::createNew(data.asArrayPtr(), data.size()); if (m_type == kEmbedded) native.m_lenUnknown10 = 2; // CORE-17010 native.inFileds(pStream); dataImageNative = native.m_data; saveInternalData(dataImageNative, L"imagenative_", L".bmp"); // PATH_TO_SAVE_INTERNAL_DATA } if ((pStrm = openStorageStream(pStorage, L"\02OlePres000")) != NULL) { dataImagePress = readStorageStream(&pStrm, 0x28); // CORE-19521 at handle 0xD773 int idx = 0; while (!isWmfByContent(dataImagePress) && idx < 10) { OdString sKey; sKey.format(L"\02OlePres%03d", ++idx); if ((pStrm = openStorageStream(pStorage, sKey.c_str())) != NULL) dataImagePress = readStorageStream(&pStrm, 0x28); else dataImagePress.clear(); } ODA_ASSERT_ONCE(isWmfByContent(dataImagePress)); } if ((pStrm = openStorageStream(pStorage, "CONTENTS")) != NULL) // ole_past_wmf.dwg { dataImageContent = readStorageStream(&pStrm, nOffsetInContents); //saveInternalData(dataImageContent, L"dataImageContent_", L".bmp"); } if ((pStrm = openStorageStream(pStorage, L"Package")) != NULL) dataPackage = readStorageStream(&pStrm); // CORE-26849 #ifdef DROP_PRES_STREAM if (dataImagePress.size()) { if (destroy(pStorage, L"\02OlePres000") != SSTG_OK) { ODA_FAIL_ONCE(); } } #endif closeStorage(&pStorage); } if (pRootStorage) closeStructuredStorage(&pRootStorage); #ifdef DROP_PRES_STREAM pMemStream->rewind(); OdUInt32 nSizeOut = (OdUInt32) pMemStream->length(); m_buff.resize(nSizeOut); pMemStream->getBytes(m_buff.asArrayPtr(), m_buff.size()); #endif if (dataImageNative.size() || dataImagePress.size() || dataImageContent.size()) { OdString sExtNative = getExtByContent(dataImageNative), sExtPress = getExtByContent(dataImagePress), sExtContent = getExtByContent(dataImageContent); OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); if (pRasSvcs.get()) { try { if (dataImageNative.size() && sExtNative != L".wmf" && sExtNative != L".emf") m_pImage = pRasSvcs->loadRasterImage( OdFlatMemStream::createNew(dataImageNative.asArrayPtr(), dataImageNative.size())); if ( m_pImage.isNull() && dataImageContent.size() && sExtContent != L".wmf" && sExtContent != L".emf" // 2\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Calibri;}} // {\*\generator Riched20 10.0.17763}\viewkind4\uc1 // \pard\sa200\sl276\slmult1\f0\fs22\lang9 OLE Creation in AutoCAD\par // \par // } // possible extract "OLE Creation in AutoCAD" but dataImagePress already contains WMF with it && sExtContent != L".unknown") // contains text with refs to font for CORE-20308 m_pImage = pRasSvcs->loadRasterImage( OdFlatMemStream::createNew(dataImageContent.asArrayPtr(), dataImageContent.size())); if (m_pImage.isNull() && dataImagePress.size() && sExtPress != L".wmf" && sExtPress != L".emf") m_pImage = pRasSvcs->loadRasterImage( OdFlatMemStream::createNew(dataImagePress.asArrayPtr(), dataImagePress.size())); } catch (OdError&) // err { ODA_FAIL_ONCE(); } } if (sExtNative == L".wmf" || sExtNative == L".emf") m_dataMetafile = dataImageNative; else if (sExtPress == L".wmf" || sExtPress == L".emf") { if (m_type == kUnknown) { m_type = kEmbedded; if (m_sUserType.isEmpty() && !dataImageContent.size()) // CORE-20308 { if (!dataPackage.size()) // CORE-26849 // possible TODO find "[Content_Types]" ".xml " in dataPackage m_type = kLink; } } m_dataMetafile = dataImagePress; } else if (sExtContent == L".wmf" || sExtContent == L".emf") m_dataMetafile = dataImageContent; if (m_type == kLink && olePaths.m_asLinkedPaths.size()) // CORE-20308 { m_sLinkPath = olePaths.m_asLinkedPaths.last(); // CORE-20462 via utf16 if (olePaths.m_asLinkedPaths.size() > 1) { m_sLinkName = olePaths.m_asLinkedPaths.first(); for (int idx = 1; idx < int(olePaths.m_asLinkedPaths.size() - 1); idx++) { if (idx == 1) { m_sLinkName = olePaths.m_asLinkedPaths[idx]; if (m_sLinkPath.find(m_sLinkName) > 0) m_sLinkName = m_sLinkPath; continue; } m_sLinkName += olePaths.m_asLinkedPaths[idx]; } } else m_sLinkName = m_sLinkPath; } ODA_ASSERT_ONCE(m_pImage.get() || m_dataMetafile.size()); } } } OdBinaryData createCompoundDocument(OdGiRasterImagePtr pImage) { OdBinaryData buff; ODA_ASSERT_ONCE(pImage.get()); if (pImage.isNull()) return buff; OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); ODA_ASSERT_ONCE(pRasSvcs.get()); if (pRasSvcs.isNull()) return buff; OdMemoryStreamPtr pMemStream = OdMemoryStream::createNew(); struct _RootStorage* pRootStorage = NULL; //#if 0 //def PATH_TO_SAVE_INTERNAL_DATA // test // OdFilePathName info(PATH_TO_SAVE_INTERNAL_DATA + L"/dummy.storage"); // if (info.exists()) // { // OdStreamBufPtr io = ::odrxSystemServices()->createFile(info.pathName()); // OdBinaryData dummyData; // dummyData.resize((OdUInt32) io->length()); // io->getBytes(dummyData.asArrayPtr(), dummyData.size()); // pMemStream->putBytes(dummyData.asArrayPtr(), dummyData.size()); // pMemStream->rewind(); // pRootStorage = openFileStorage(pMemStream.get(), STG_RW); // } // else //#endif pRootStorage = createFileStorage(pMemStream.get()); // createStructuredStorageInSource ( Storage* pStorage = NULL; if (pRootStorage && (pStorage = getStorage(pRootStorage)) != NULL) { //StgInfo* pInfo = NULL; //if (getStorageInfo(pStorage, &pInfo) == SSTG_OK) //{ // OdString sName = (const wchar_t*) pInfo->sName; // ODA_ASSERT_ONCE(sName == "Root Entry"); //} SSRW_CLSID clsid = OD_PBRUSH_CLSID; if (storageSetClass(pStorage, &clsid) != SSTG_OK) { ODA_FAIL_ONCE(); } OdBinaryData data; OdUInt32 len = 0; OdMemoryStreamPtr pStream; Stream* pStrm = NULL; pStream = OdMemoryStream::createNew(); OdOleSsCompObj compObj; compObj.outFileds(pStream); len = (OdUInt32) pStream->length(); data.resize(len); pStream->rewind(); pStream->getBytes(data.asArrayPtr(), data.size()); if ((pStrm = createStorageStream(pStorage, L"\01CompObj")) != NULL) writeStorageStream(&pStrm, data); pStream = OdMemoryStream::createNew(); OdOleSsNative native(pImage); native.outFileds(pStream); len = (OdUInt32) pStream->length(); data.resize(len); pStream->rewind(); pStream->getBytes(data.asArrayPtr(), data.size()); if ((pStrm = createStorageStream(pStorage, L"\01Ole10Native")) != NULL) writeStorageStream(&pStrm, data); pStream = OdMemoryStream::createNew(); OdOleSsOle ole; ole.outFileds(pStream); len = (OdUInt32) pStream->length(); data.resize(len); pStream->rewind(); pStream->getBytes(data.asArrayPtr(), data.size()); if ((pStrm = createStorageStream(pStorage, L"\01Ole")) != NULL) writeStorageStream(&pStrm, data); closeStorage(&pStorage); closeStructuredStorage(&pRootStorage); len = (OdUInt32) pMemStream->length(); buff.resize(len); pMemStream->rewind(); pMemStream->getBytes(buff.asArrayPtr(), buff.size()); } return buff; } OdUInt32 OdOleSsItemHandlerImpl::getCompoundDocumentDataSize() const { ODA_ASSERT_ONCE(m_pImage.isNull() || m_buff.size()); // m_buff should be created via createCompoundDocument return m_buff.size(); } void OdOleSsItemHandlerImpl::getCompoundDocument(OdStreamBuf& stream) const { if (m_buff.size()) stream.putBytes(m_buff.asArrayPtr(), m_buff.size()); } class OdGiMetafileImpl : public OdGiMetafile { OdUInt8* m_pBytes; OdUInt32 m_size; public: OdGiMetafileImpl() : m_pBytes(NULL) , m_size(0) { } void init(OdUInt8* /*pBytes*/, OdUInt32 /*size*/) { ODA_FAIL_ONCE(); // TODO //m_pBytes = pBytes; //m_size = size; } OdUInt32 dataSize() const override { return m_size; } void bitsData(OdUInt8* pBytes) const override { pBytes = m_pBytes; } const OdUInt8* bitsData() const override { return m_pBytes; } }; #if !defined(OLESS_WIN_GDI_SUPPORT) || !defined(ODA_WINDOWS) bool OdOleSsItemHandlerImpl::draw(const OdGiCommonDraw&, // drawObj void*, // hdc const OdGsDCRect&) const // rect { return false; } #else bool OdOleSsItemHandlerImpl::draw(const OdGiCommonDraw& /*drawObj*/, void* hdc, const OdGsDCRect& rect) const { // draw m_pImage on HDC for Windows // CORE-14810 for OdaMfcApp in Win CGI //see also CPreviewGiRasterImage::OnPaint() CDC* pDC = CDC::FromHandle((HDC) hdc); CDC& dc = *pDC; // CPaintDC dc(pWnd); CRect cliArea; cliArea.SetRect(rect.m_min.x, rect.m_max.y, rect.m_max.x, rect.m_min.y); // left, top, right, bottom dc.FillSolidRect(&cliArea, ::GetSysColor(COLOR_BTNFACE)); if (m_pImage.get()) { // Convert to BGR representation OdSmartPtr pDesc = OdGiRasterImageDesc::createObject(m_pImage->pixelWidth(), m_pImage->pixelHeight(), 24); pDesc->setScanLinesAlignment(4); pDesc->pixelFormat().setBGR(); OdGiRasterImagePtr pAcImage = m_pImage->convert(true, 50.0, 50.0, 0.0, 0, false, false, false, pDesc); // Convert to DIB representation BITMAPINFO biInfo; ZeroMemory(&biInfo, sizeof(BITMAPINFO)); biInfo.bmiHeader.biBitCount = 24; biInfo.bmiHeader.biHeight = pAcImage->pixelHeight(); biInfo.bmiHeader.biPlanes = 1; biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); biInfo.bmiHeader.biSizeImage = pAcImage->scanLineSize() * pAcImage->pixelHeight(); biInfo.bmiHeader.biWidth = pAcImage->pixelWidth(); // Compute output rectangle CRect outArea(0, 0, m_pImage->pixelWidth(), m_pImage->pixelHeight()); ////if (outArea.Width() > cliArea.Width()) { float scale = (float)outArea.Width() / cliArea.Width(); outArea.right = (int)(outArea.right / scale); outArea.bottom = (int)(outArea.bottom / scale); } //if (outArea.Height() > cliArea.Height()) { float scale = (float)outArea.Height() / cliArea.Height(); outArea.right = (int)(outArea.right / scale); outArea.bottom = (int)(outArea.bottom / scale); } #if defined _WIN32_WCE outArea.MoveToXY(cliArea.CenterPoint().x - outArea.Width() / 2, cliArea.CenterPoint().y - outArea.Height() / 2); ::SetStretchBltMode(dc.GetSafeHdc(), COLORONCOLOR); #else outArea.MoveToXY(cliArea.CenterPoint().x - outArea.Width() / 2, cliArea.CenterPoint().y - outArea.Height() / 2); ::SetStretchBltMode(dc.GetSafeHdc(), HALFTONE); #endif if (pAcImage->scanLines()) ::StretchDIBits(dc.GetSafeHdc(), outArea.left, outArea.top, outArea.Width(), outArea.Height(), 0, 0, pAcImage->pixelWidth(), pAcImage->pixelHeight(), pAcImage->scanLines(), &biInfo, DIB_RGB_COLORS, SRCCOPY); else { OdUInt8Array tmpArray; tmpArray.resize(biInfo.bmiHeader.biSizeImage); pAcImage->scanLines(tmpArray.asArrayPtr(), 0, pAcImage->pixelHeight()); ::StretchDIBits(dc.GetSafeHdc(), outArea.left, outArea.top, outArea.Width(), outArea.Height(), 0, 0, pAcImage->pixelWidth(), pAcImage->pixelHeight(), tmpArray.getPtr(), &biInfo, DIB_RGB_COLORS, SRCCOPY); } } return true; } #endif // of else !defined(OLESS_WIN_GDI_SUPPORT) || !defined(ODA_WINDOWS) bool OdOleSsItemHandlerImpl::draw(OdGiConveyorGeometry* pDestGeom, //# ifdef USE_METAFILE_SUPPORT OdGiConveyorContext* pContext, OdGiRegenType regenType, //# else // OdGiConveyorContext*, OdGiRegenType, //# endif const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, //# ifdef USE_METAFILE_SUPPORT const OdGsDCRect& rect, //# else // const OdGsDCRect&, //# endif bool isExport) // = false { if ( !u.isZeroLength() && !v.isZeroLength() && (m_pImage.get() || m_dataMetafile.size())) { OdGiRasterImage* pImage = m_pImage.get(); //# ifdef USE_METAFILE_SUPPORT if (!pImage) { pImage = getRaster(false).get(); // contains test of m_bImageCacheChecked if (!pImage) { OdBinaryData data; OdString sExt; bool bRes = getImageData(data, &sExt); if (bRes) drawMetafile(data, sExt, pDestGeom, pContext, regenType, origin, u, v, rect, isExport); } } //# endif if (pImage) pDestGeom->rasterImageProc( origin, u / (double) pImage->pixelWidth(), v / (double) pImage->pixelHeight(), pImage, 0, 0, !isExport); //else if (m_dataMetafile.size()) //{ // TODO //OdStaticRxObject wmfMetafile; //wmfMetafile.init(const_cast(m_dataMetafile.asArrayPtr()), m_dataMetafile.size()); //pDestGeom->metafileProc( // origin, // u, // / fabs((double) rect.m_max.x - rect.m_min.x), // v, // / fabs((double) rect.m_max.y - rect.m_min.y), // &wmfMetafile); //} } return true; } bool OdGiSelfGiDrawablePEImpl::draw(OdRxObject* pRxObject, OdGiConveyorGeometry* pDestGeom, OdGiConveyorContext* pContext, OdGiRegenType regenType, const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGsDCRect& rect, bool isExport) const // = false { OdOleSsItemHandlerImplPtr pItem = OdOleSsItemHandlerImpl::cast(pRxObject); ODA_ASSERT_ONCE(pItem.get()); if (pItem.get()) return pItem->draw(pDestGeom, pContext, regenType, origin, u, v, rect, isExport); return true; } OdOleItemHandler::Type OdOleSsItemHandlerImpl::type() const { return m_type; } OdOleItemHandler::DvAspect OdOleSsItemHandlerImpl::drawAspect() const { return OdOleItemHandler::DvAspect(m_drawAspect); } OdString OdOleSsItemHandlerImpl::userType() const { return m_sUserType; } OdString OdOleSsItemHandlerImpl::linkName() const { return m_sLinkName; } OdString OdOleSsItemHandlerImpl::linkPath() const { return m_sLinkPath; } bool OdOleSsItemHandlerImpl::embedRaster(OdGiRasterImagePtr pImage, OdRxObject*) // pRxDb = NULL { m_pImage = pImage; m_type = kEmbedded; m_sUserType = L"Paintbrush Picture"; m_sLinkName = m_sLinkPath = OdString::kEmpty; ODA_ASSERT_ONCE(m_pImage.get()); m_buff = createCompoundDocument(m_pImage); setDrawAspect(kContent); return !m_pImage.isNull(); } //bool OdOleSsItemHandlerImpl::embedRasterFromFile(const OdString& path) //{ // OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); // ODA_ASSERT_ONCE(pRasSvcs.get()); // if (pRasSvcs.isNull()) // return false; // return embedRaster(pRasSvcs->loadRasterImage(path)); //} OdGiRasterImagePtr OdOleSsItemHandlerImpl::getRaster(bool) const // bDisplayedOnly = true { //#ifdef USE_METAFILE_SUPPORT if (m_pImage.isNull()) { if (!m_bImageCacheChecked) { m_bImageCacheChecked = true; OdBinaryData data; OdString sExt; bool bRes = getImageData(data, &sExt); if (bRes) m_pImageCache = getMetafileRaster(data, sExt); } return m_pImageCache; // last gotten via metafile conversion } //#endif return m_pImage; } bool OdOleSsItemHandlerImpl::getImageData(OdBinaryData& data, OdString* psExtSuffix) const // = NULL { data = m_dataMetafile; if (!m_dataMetafile.size()) { if (m_pImage.get()) { OdRxRasterServicesPtr pRasSvcs = ::odrxDynamicLinker()->loadApp(RX_RASTER_SERVICES_APPNAME); if (pRasSvcs.isNull()) return false; OdMemoryStreamPtr pStreamBuf = OdMemoryStream::createNew(); if ( !pRasSvcs->isRasterImageTypeSupported(OdRxRasterServices::kPNG) || !pRasSvcs->convertRasterImage(m_pImage, OdRxRasterServices::kPNG, pStreamBuf)) return false; pStreamBuf->rewind(); OdUInt32 nLen = static_cast(pStreamBuf->length()); data.resize(nLen); pStreamBuf->getBytes(data.asArrayPtr(), nLen); if (psExtSuffix) *psExtSuffix = L".png"; return true; } return false; } if (psExtSuffix) *psExtSuffix = getExtByContent(m_dataMetafile); return true; }