/////////////////////////////////////////////////////////////////////////////// // 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. /////////////////////////////////////////////////////////////////////////////// /************************************************************************/ /* This console application reads a DWG/DXF file */ /* and saves it to a SVG file */ /* */ /* Calling sequence: */ /* */ /* OdSvgExportEx */ /* */ /************************************************************************/ #ifdef WD_WIN_SYSTEM #include #include #endif #include #include #include #include #include #define STL_USING_IOSTREAM #include "OdaSTL.h" #define STD(a) std:: a #include "OdaCommon.h" #include "DbDatabase.h" #include "RxObjectImpl.h" #include "ExSystemServices.h" #include "ExHostAppServices.h" #include "OdFileBuf.h" #include "ColorMapping.h" #include "RxDynamicModule.h" #include "RxVariantValue.h" #include "GiContextForDbDatabase.h" #include "DbGsManager.h" #include "DbViewportTableRecord.h" #include "DbBlockTableRecord.h" #include "../Exports/2dExport/Include/2dExportDevice.h" //#ifdef linux #include //#endif #ifdef OD_HAVE_CONSOLE_H_FILE #include #endif //#define SVG_WORLD_COORDINATES //#define SVG_WHOLE_DRAWING /************************************************************************/ /* Define a module map for statically linked modules: */ /************************************************************************/ #ifndef _TOOLKIT_IN_DLL_ ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(SvgExportModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(ModelerModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdRecomputeDimBlockModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(BitmapModule); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdRasterProcessingServicesImpl); ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(ExRasterModule); ODRX_BEGIN_STATIC_MODULE_MAP() ODRX_DEFINE_STATIC_APPLICATION(OdSvgExportModuleName, SvgExportModule) ODRX_DEFINE_STATIC_APPLICATION(OdModelerGeometryModuleName, ModelerModule) ODRX_DEFINE_STATIC_APPLICATION(OdRecomputeDimBlockModuleName, OdRecomputeDimBlockModule) ODRX_DEFINE_STATIC_APPMODULE(OdWinBitmapModuleName, BitmapModule) ODRX_DEFINE_STATIC_APPMODULE(OdRasterProcessorModuleName, OdRasterProcessingServicesImpl) ODRX_DEFINE_STATIC_APPMODULE(RX_RASTER_SERVICES_APPNAME, ExRasterModule) ODRX_END_STATIC_MODULE_MAP() #endif #if defined(ODA_WINDOWS) #define MAX_PATH_LENGTH 1024 #define SMALL_STRING_SIZE 32 #define LARGE_STRING_SIZE 1024 bool GetRegistryString(HKEY key, const wchar_t* subkey, const wchar_t* name, wchar_t* value, int size) { bool rv = false; HKEY hKey; if (RegOpenKeyExW(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) { DWORD dwSize(MAX_PATH_LENGTH); unsigned char data[MAX_PATH_LENGTH]; memset(&data, 0x00, MAX_PATH_LENGTH); if (RegQueryValueExW(hKey, name, 0, 0, &data[0], &dwSize) == ERROR_SUCCESS) { rv = true; } else { if (ERROR_SUCCESS == RegEnumKeyExW(hKey, 0, (LPWSTR)(unsigned short*)&data[0], &dwSize , NULL, NULL, NULL, NULL)) { rv = true; } } if (size < MAX_PATH_LENGTH) { swprintf(value, size, L"%ls\0", (const wchar_t*)data); } else { wcsncpy(value, (wchar_t*)data, size - 1); value[size - 1] = '\0'; } RegCloseKey(hKey); } return rv; } ////////////////////////////////////////////////////////////////////// OdString GetRegistryAcadProfilesKey() { OdString subkey = L"SOFTWARE\\Autodesk\\AutoCAD"; TCHAR version[SMALL_STRING_SIZE]; TCHAR subVersion[SMALL_STRING_SIZE]; TCHAR profile[LARGE_STRING_SIZE]; // char searchPaths[ LARGE_STRING_SIZE]; if (GetRegistryString(HKEY_CURRENT_USER, (LPCTSTR)subkey, OD_T("CurVer"), version, SMALL_STRING_SIZE) == 0) return L""; subkey += L"\\"; subkey += version; // get the sub-version and concatenate onto subkey if (GetRegistryString(HKEY_CURRENT_USER, (LPCTSTR)subkey, OD_T("CurVer"), subVersion, SMALL_STRING_SIZE) == 0) return L""; subkey += L"\\"; subkey += subVersion; subkey += L"\\Profiles"; // get the value for the (Default) entry in the registry if (GetRegistryString(HKEY_CURRENT_USER, (LPCTSTR)subkey, OD_T(""), profile, LARGE_STRING_SIZE) == 0) return L""; subkey += L"\\"; subkey += profile; return subkey; } OdString GetRegistryACADFromProfile() { static OdString paths; if (!paths.isEmpty()) return paths; OdString subkey; TCHAR searchPaths[LARGE_STRING_SIZE]; TCHAR expandedPath[LARGE_STRING_SIZE]; subkey = GetRegistryAcadProfilesKey(); if (!subkey.isEmpty()) { subkey += L"\\General"; // get the value for the ACAD entry in the registry if (GetRegistryString(HKEY_CURRENT_USER, (LPCTSTR)subkey, OD_T("ACAD"), searchPaths, LARGE_STRING_SIZE) == 0) return L""; ExpandEnvironmentStrings(searchPaths, expandedPath, LARGE_STRING_SIZE); paths = OdString(expandedPath); return paths; } else return L""; } #endif //---------------------------------------------------------------------------------- class MyServices : public ExSystemServices, public ExHostAppServices { protected: ODRX_USING_HEAP_OPERATORS(ExSystemServices); private: /************************************************************************/ /* Compile password support for R18 Drawings */ /************************************************************************/ #if defined(_MSC_VER) && !defined(_WINRT) bool getPassword(const OdString& dwgName, bool /*isXref*/, OdPassword& password) { char buff[_MAX_PATH] = {0}; STD(cout) << "Enter password to open drawing: " << STD(endl); ::CharToOemA((const char*)dwgName, buff); STD(cout) << " " << buff << STD(endl); STD(cin).get(buff, '\n').get(); wchar_t pwdBuff[_MAX_PATH] = {0}; // convert to upper case unicode: ::OemToCharW( buff, pwdBuff ); ::CharUpperW( pwdBuff ); password = pwdBuff; return !password.isEmpty(); } #endif public: OdGsDevicePtr gsBitmapDevice(OdRxObject* /*pViewObj*/ = NULL, OdDbBaseDatabase* /*pDb*/ = NULL, OdUInt32 flags = 0) { OdGsModulePtr pM; if (GETBIT(flags, kFor2dExportRender)) { if (GETBIT(flags, kFor2dExportRenderHLR)) return OdGsDevicePtr(); pM = ::odrxDynamicLinker()->loadModule(OdWinOpenGLModuleName); } if (pM.isNull()) pM = ::odrxDynamicLinker()->loadModule(OdWinBitmapModuleName); if (pM.isNull()) return OdGsDevicePtr(); return pM->createBitmapDevice(); } virtual OdString findFile( const OdString& filename, OdDbBaseDatabase* pDb = 0, FindFileHint hint = kDefault) { OdString sFilePath = ExHostAppServices::findFile(filename, pDb, hint); if (!sFilePath.isEmpty()) return sFilePath; if (hint == kFontFile || hint == kCompiledShapeFile) { OdString sFileName = filename; OdString ext = sFileName.right(4); ext.makeUpper(); if (ext != OD_T(".SHX")) sFileName += OD_T(".shx"); #if defined(ODA_WINDOWS) OdString sPaths = GetRegistryACADFromProfile(); if (sPaths.isEmpty()) return sFilePath; OdDbSystemServices* pSs = odSystemServices(); while (!sPaths.isEmpty()) { int nFindStr = sPaths.find(L";"); if (-1 == nFindStr) break; OdString sPath; sPath = sPaths.left(nFindStr); sPath += L"\\"; sPath += sFileName; if (pSs->accessFile(sPath, Oda::kFileRead)) return sPath; sPaths = sPaths.right(sPaths.getLength() - nFindStr - 1); } #endif } return sFilePath; } }; /************************************************************************/ /* Main */ /************************************************************************/ #if defined(OD_USE_WMAIN) int wmain(int argc, wchar_t* argv[]) #else int main(int argc, char* argv[]) #endif { #ifdef OD_HAVE_CCOMMAND_FUNC argc = ccommand(&argv); #endif if (argc < 3) { STD(cout) << "usage: OdSvgExportEx " << STD(endl); } else { /********************************************************************/ /* For correct Unicode translation, apply the current system locale.*/ /********************************************************************/ setlocale(LC_ALL, ""); /********************************************************************/ /* But use usual conversion for scanf()/sprintf() */ /********************************************************************/ setlocale(LC_NUMERIC, "C"); #ifndef _TOOLKIT_IN_DLL_ ODRX_INIT_STATIC_MODULE_MAP(); #endif /********************************************************************/ /* Create a custom Services instance. */ /********************************************************************/ OdStaticRxObject svcs; odInitialize(&svcs); /********************************************************************/ /* Display the Product and Version that created the executable */ /********************************************************************/ STD(cout) << "Developed using " << (const char*)svcs.product() << ", " << (const char*)svcs.versionString() << STD(endl); try { /******************************************************************/ /* Create a database and load the drawing into it. */ /******************************************************************/ OdDbDatabasePtr pDb = svcs.readFile(argv[1]); if (!pDb.isNull()) { OdGsModulePtr pModule = ::odrxDynamicLinker()->loadModule(OdSvgExportModuleName, false); OdGsDevicePtr dev = pModule->createDevice(); if (!dev.isNull()) { OdStreamBufPtr file = odSystemServices()->createFile(argv[2], Oda::kFileWrite, Oda::kShareDenyNo, Oda::kCreateAlways); // Setup output stream dev->properties()->putAt("Output", file.get()); // where to put images OdString base = argv[2]; int slash1 = base.reverseFind(L'/'); int slash2 = base.reverseFind(L'\\'); base = base.left(odmax(slash1, slash2)); dev->properties()->putAt(OD_T("ImageBase"), OdRxVariantValue(base)); // default extension for generated images (e.g. from OLE) dev->properties()->putAt(OD_T("DefaultImageExt"), OdRxVariantValue(OdString(".png"))); // size of pixel in device units dev->properties()->putAt("LineWeightScale", OdRxVariantValue(1.0)); // prefix to prepend to image names dev->properties()->putAt("ImageUrl", OdRxVariantValue( OdString("./"))); // "ColorPolicy" - 0 = Colors are unchanged, 1 = Drawing is converted to monochrome, 2 = Drawing is converted to grayscale dev->properties()->putAt("ColorPolicy", OdRxVariantValue((OdInt32)0)); // "ExplodeShxTexts" - true = shx text is split into separate lines // false = for shx text a suitable alternative is found and exported as text dev->properties()->putAt("ExplodeShxTexts", OdRxVariantValue(true)); // "UseLineTypes" - true = for lines with types svg types applied, such as stroke-dasharray // false = lines with types are broken into separate lines dev->properties()->putAt(OD_T("UseLineTypes"), OdRxVariantValue(true)); // Set active palette OdRxVariantValue tmpVar = (OdRxVariantValue)dev->properties()->getAt("BackGround"); ODCOLORREF bg = tmpVar.get()->getInt32(); dev->setBackgroundColor(bg); dev->setLogicalPalette(odcmAcadLightPalette(), 256); // Prepare database context for device OdGiContextForDbDatabasePtr pDwgContext = OdGiContextForDbDatabase::createObject(); pDwgContext->setDatabase(pDb); // do not render paper background and borders pDwgContext->setPlotGeneration(true); pDwgContext->setPaletteBackground(bg); // Avoid tessellation if possible pDwgContext->setHatchAsPolygon(OdGiDefaultContext::kHatchPolygon); // Set minimal line width in SVG so that too thin line would not disappear dev->properties()->putAt(L"MinimalWidth", OdRxVariantValue(0.1)); // load plotstyle tables (as set in layout settings) OdDbBaseDatabasePEPtr pBaseDatabase(pDb); pBaseDatabase->loadPlotstyleTableForActiveLayout(pDwgContext, pDb); // Prepare the device to render the active layout in this database. OdGsDevicePtr wrapper = OdDbGsManager::setupActiveLayoutViews( dev, pDwgContext ); // creating render device for rendering the shaded viewports TD_2D_EXPORT::Od2dExportDevice* pDeviceSvg = (TD_2D_EXPORT::Od2dExportDevice*)dev.get(); pDeviceSvg->setRenderDevice(); OdRxObjectPtr pLayout = pBaseDatabase->currentLayout(pDb); // copy lineweight display override flag from the layout pDeviceSvg->properties()->putAt("PlotLineweights", OdRxVariantValue(OdDbBaseLayoutPEPtr(pLayout)->printLineweights(pLayout))); for (int nDevice = 0; nDevice < pDeviceSvg->numRenderDevices(); nDevice++) { OdGsDevice* pRenderDevice = pDeviceSvg->renderDeviceAt(nDevice); //pRenderDevice->setBackgroundColor(bg); pRenderDevice->setLogicalPalette(odcmAcadLightPalette(), 256); OdGsDevicePtr pLayoutHelper = pBaseDatabase->setupActiveLayoutViews(pRenderDevice, pDwgContext); pDeviceSvg->setRenderLayoutHelperAt(nDevice, pLayoutHelper.get()); } // Setup device coordinate space #if defined(SVG_WORLD_COORDINATES) // this mode illustrates preserving world coordinates in svg // used for interaction with InkScape-like vector editors using svg as a base format // note that the drawing will look upside-down in svg viewer { pDb->setTILEMODE(true); OdDbBlockTableRecordPtr ms = pDb->getModelSpaceId().safeOpenObject(); OdGeExtents3d ext; ms->getGeomExtents(ext); // calculate minimal square viewport size covering all the model double x_max = odmax(-ext.minPoint().x, ext.maxPoint().x); double y_max = odmax(-ext.minPoint().y, ext.maxPoint().y); double size = ceil(odmax(x_max, y_max))*2; // set the active viewport to that square OdDbViewportTableRecordPtr vp = pDb->activeViewportId().safeOpenObject(OdDb::kForWrite); vp->setWidth(size); vp->setHeight(size); vp->setViewDirection(OdGeVector3d::kZAxis); vp->setTarget(OdGePoint3d::kOrigin); vp->setCenterPoint(OdGePoint2d::kOrigin); OdGsDCRect screenRect(OdGsDCPoint(-size/2, -size/2), OdGsDCPoint(size/2, size/2)); wrapper->onSize(screenRect); } #elif defined(SVG_WHOLE_DRAWING) // this mode illustrates exporting whole drawing, ignoring current viewport { pDb->setTILEMODE(true); OdDbObjectId id = pDb->activeViewportId(); if (!id.isNull()) { OdDbObjectPtr pVp = id.safeOpenObject(OdDb::kForWrite); OdAbstractViewPEPtr pAVP(pVp); pAVP->zoomExtents(pVp); } wrapper->onSize(OdGsDCRect(0, 1024, 768, 0)); // svg coordinates are upside-down } #else // just export the current viewport wrapper->onSize(OdGsDCRect(0, 1024, 768, 0)); // svg coordinates are upside-down #endif // Initiate rendering. wrapper->update( 0 ); } } } catch (OdError& err) { OdString msg = err.description(); STD(cout) << "Teigha Error: " << (const char*)msg << STD(endl) << STD(endl); } catch (...) { STD(cout) << "Unknown Error." << STD(endl) << STD(endl); return 0; } odUninitialize(); } return 0; }