/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #define LOK_USE_UNSTABLE_API #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Dirty hack -- we go directly into sw -- ideally we need some sort of // layer to get the writer shell for tiled rendering #include #include #include #include #include // And let's also grab the SvpSalInstance and SvpSalVirtualDevice #include #include #include using namespace css; using namespace utl; using namespace boost; struct LibLODocument_Impl; struct LibLibreOffice_Impl; static LibLibreOffice_Impl *gImpl = NULL; static weak_ptr< LibreOfficeKitClass > gOfficeClass; static weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass; typedef struct { const char *extn; const char *filterName; } ExtensionMap; // We need a shared_array for passing into the BitmapDevice (via // VirtualDevice.SetOutputSizePixelScaleOffsetAndBuffer which goes via the // SvpVirtualDevice, ending up in the basebmp BitmapDevice. However as we're // given the array externally we can't delete it, and hence need to override // shared_array's default of deleting its pointer. template struct NoDelete { void operator()(T* /* p */) {} }; static const ExtensionMap aWriterExtensionMap[] = { { "doc", "MS Word 97" }, { "docx", "MS Word 2007 XML" }, { "fodt", "OpenDocument Text Flat XML" }, { "html", "HTML (StarWriter)" }, { "odt", "writer8" }, { "ott", "writer8_template" }, { "pdf", "writer_pdf_Export" }, { "txt", "Text" }, { "xhtml", "XHTML Writer File" }, { NULL, NULL } }; static const ExtensionMap aCalcExtensionMap[] = { { "csv", "Text - txt - csv (StarCalc)" }, { "fods", "OpenDocument Spreadsheet Flat XML" }, { "html", "HTML (StarCalc)" }, { "ods", "calc8" }, { "ots", "calc8_template" }, { "pdf", "calc_pdf_Export" }, { "xhtml", "XHTML Calc File" }, { "xls", "MS Excel 97" }, { "xlsx", "Calc MS Excel 2007 XML" }, { NULL, NULL } }; static const ExtensionMap aImpressExtensionMap[] = { { "fodp", "OpenDocument Presentation Flat XML" }, { "html", "impress_html_Export" }, { "odg", "impress8_draw" }, { "odp", "impress8" }, { "otp", "impress8_template" }, { "pdf", "impress_pdf_Export" }, { "potm", "Impress MS PowerPoint 2007 XML Template" }, { "pot", "MS PowerPoint 97 Vorlage" }, { "pptx", "Impress MS PowerPoint 2007 XML" }, { "pps", "MS PowerPoint 97 Autoplay" }, { "ppt", "MS PowerPoint 97" }, { "svg", "impress_svg_Export" }, { "swf", "impress_flash_Export" }, { "xhtml", "XHTML Impress File" }, { NULL, NULL } }; static const ExtensionMap aDrawExtensionMap[] = { { "odg", "draw8" }, { "fodg", "draw_ODG_FlatXML" }, { "html", "draw_html_Export" }, { "svg", "draw_svg_Export" }, { "swf", "draw_flash_Export" }, { "xhtml", "XHTML Draw File" }, { "vdx", "draw_Visio_Document" }, { "vsd", "draw_Visio_Document" }, { "vsdm", "draw_Visio_Document" }, { "vsdx", "draw_Visio_Document" }, { "pub", "draw_Publisher_Document" }, { "cdr", "draw_CorelDraw_Document" }, { "wpg", "draw_WordPerfect_Graphics" }, { NULL, NULL } }; static OUString getUString(const char* pString) { if (pString == NULL) return OUString(); OString sString(pString, strlen(pString)); return OStringToOUString(sString, RTL_TEXTENCODING_UTF8); } // Try to convert a relative URL to an absolute one static OUString getAbsoluteURL(const char* pURL) { OUString aURL( getUString( pURL ) ); OUString sAbsoluteDocUrl, sWorkingDir, sDocPathUrl; // FIXME: this would appear to kill non-file URLs. osl_getProcessWorkingDir(&sWorkingDir.pData); osl::FileBase::getFileURLFromSystemPath( aURL, sDocPathUrl ); osl::FileBase::getAbsoluteFileURL(sWorkingDir, sDocPathUrl, sAbsoluteDocUrl); return sAbsoluteDocUrl; } extern "C" { static void doc_destroy(LibreOfficeKitDocument* pThis); static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions); static LibreOfficeKitDocumentType doc_getDocumentType(LibreOfficeKitDocument* pThis); static int doc_getNumberOfParts(LibreOfficeKitDocument* pThis); static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart); void doc_paintTile(LibreOfficeKitDocument* pThis, unsigned char* pBuffer, const int nCanvasWidth, const int nCanvasHeight, int* pRowStride, const int nTilePosX, const int nTilePosY, const int nTileWidth, const int nTileHeight); static void doc_getDocumentSize(LibreOfficeKitDocument* pThis, long* pWidth, long* pHeight); struct LibLODocument_Impl : public _LibreOfficeKitDocument { uno::Reference mxComponent; shared_ptr< LibreOfficeKitDocumentClass > m_pDocumentClass; LibLODocument_Impl(const uno::Reference &xComponent) : mxComponent( xComponent ) { if (!(m_pDocumentClass = gDocumentClass.lock())) { m_pDocumentClass.reset(new LibreOfficeKitDocumentClass); m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocument); m_pDocumentClass->destroy = doc_destroy; m_pDocumentClass->saveAs = doc_saveAs; m_pDocumentClass->getDocumentType = doc_getDocumentType; m_pDocumentClass->getNumberOfParts = doc_getNumberOfParts; m_pDocumentClass->setPart = doc_setPart; m_pDocumentClass->paintTile = doc_paintTile; m_pDocumentClass->getDocumentSize = doc_getDocumentSize; gDocumentClass = m_pDocumentClass; } pClass = m_pDocumentClass.get(); } ~LibLODocument_Impl() { mxComponent->dispose(); } }; static void doc_destroy(LibreOfficeKitDocument *pThis) { LibLODocument_Impl *pDocument = static_cast(pThis); delete pDocument; } static void lo_destroy (LibreOfficeKit* pThis); static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath); static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL); static char * lo_getError (LibreOfficeKit* pThis); struct LibLibreOffice_Impl : public _LibreOfficeKit { OUString maLastExceptionMsg; shared_ptr< LibreOfficeKitClass > m_pOfficeClass; LibLibreOffice_Impl() { if(!(m_pOfficeClass = gOfficeClass.lock())) { m_pOfficeClass.reset(new LibreOfficeKitClass); m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass); m_pOfficeClass->destroy = lo_destroy; m_pOfficeClass->documentLoad = lo_documentLoad; m_pOfficeClass->getError = lo_getError; gOfficeClass = m_pOfficeClass; } pClass = m_pOfficeClass.get(); } }; // Wonder global state ... static uno::Reference xContext; static uno::Reference xSFactory; static uno::Reference xFactory; static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL) { LibLibreOffice_Impl* pLib = static_cast(pThis); OUString aURL = getAbsoluteURL(pURL); uno::Reference xComponentLoader = frame::Desktop::create(xContext); pLib->maLastExceptionMsg = ""; try { uno::Reference xComponent; xComponent = xComponentLoader->loadComponentFromURL( aURL, OUString("_blank"), 0, uno::Sequence()); if (xComponent.is()) return new LibLODocument_Impl(xComponent); else pLib->maLastExceptionMsg = "unknown load failure"; } catch (const uno::Exception& exception) { pLib->maLastExceptionMsg = exception.Message; } return NULL; } static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions) { LibLODocument_Impl* pDocument = static_cast(pThis); OUString sFormat = getUString(pFormat); OUString aURL = getAbsoluteURL(sUrl); try { const ExtensionMap* pMap; switch (doc_getDocumentType(pThis)) { case LOK_DOCTYPE_SPREADSHEET: pMap = (const ExtensionMap*) aCalcExtensionMap; break; case LOK_DOCTYPE_PRESENTATION: pMap = (const ExtensionMap*) aImpressExtensionMap; break; case LOK_DOCTYPE_DRAWING: pMap = (const ExtensionMap*) aDrawExtensionMap; break; case LOK_DOCTYPE_TEXT: pMap = (const ExtensionMap*) aWriterExtensionMap; break; case LOK_DOCTYPE_OTHER: return false; } if (pFormat == NULL) { // sniff from the extension sal_Int32 idx = aURL.lastIndexOf("."); if( idx > 0 ) { sFormat = aURL.copy( idx + 1 ); } else { gImpl->maLastExceptionMsg = "input filename without a suffix"; return false; } } OUString aFilterName; for (sal_Int32 i = 0; pMap[i].extn; ++i) { if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn)) { aFilterName = getUString(pMap[i].filterName); break; } } if (aFilterName.isEmpty()) { gImpl->maLastExceptionMsg = "no output filter found for provided suffix"; return false; } OUString aFilterOptions = getUString(pFilterOptions); MediaDescriptor aSaveMediaDescriptor; aSaveMediaDescriptor["Overwrite"] <<= sal_True; aSaveMediaDescriptor["FilterName"] <<= aFilterName; aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS()] <<= aFilterOptions; uno::Reference xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW); xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList()); return true; } catch (const uno::Exception& exception) { gImpl->maLastExceptionMsg = "exception: " + exception.Message; } return false; } static LibreOfficeKitDocumentType doc_getDocumentType (LibreOfficeKitDocument* pThis) { LibLODocument_Impl* pDocument = static_cast(pThis); try { uno::Reference xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW); uno::Sequence aSequence = xDocument->getArgs(); MediaDescriptor aMediaDescriptor(aSequence); OUString sPropertyName = MediaDescriptor::PROP_DOCUMENTSERVICE(); OUString aDocumentService = aMediaDescriptor.getUnpackedValueOrDefault(sPropertyName, OUString()); if (aDocumentService.isEmpty()) { gImpl->maLastExceptionMsg = "unknown document type"; return LOK_DOCTYPE_OTHER; } if (aDocumentService == "com.sun.star.sheet.SpreadsheetDocument") { return LOK_DOCTYPE_SPREADSHEET; } else if (aDocumentService == "com.sun.star.presentation.PresentationDocument") { return LOK_DOCTYPE_PRESENTATION; } else if (aDocumentService == "com.sun.star.drawing.DrawingDocument") { return LOK_DOCTYPE_DRAWING; } else if (aDocumentService == "com.sun.star.text.TextDocument") { return LOK_DOCTYPE_TEXT; } else { gImpl->maLastExceptionMsg = "unknown document mapping"; } } catch (const uno::Exception& exception) { gImpl->maLastExceptionMsg = "exception: " + exception.Message; } return LOK_DOCTYPE_OTHER; } static int doc_getNumberOfParts (LibreOfficeKitDocument* pThis) { (void) pThis; // Assume writer document for now. return 1; } static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart) { (void) pThis; (void) nPart; } void doc_paintTile (LibreOfficeKitDocument* pThis, unsigned char* pBuffer, const int nCanvasWidth, const int nCanvasHeight, int* pRowStride, const int nTilePosX, const int nTilePosY, const int nTileWidth, const int nTileHeight) { LibLODocument_Impl* pDocument = static_cast(pThis); Application::AcquireSolarMutex(1); switch (doc_getDocumentType(pThis)) { case LOK_DOCTYPE_TEXT: { SwXTextDocument* pTxtDoc = dynamic_cast< SwXTextDocument* >( pDocument->mxComponent.get() ); SwDocShell* pDocShell = pTxtDoc->GetDocShell(); SwDoc* pDoc = pDocShell->GetDoc(); SwViewShell* pViewShell = pDoc->GetCurrentViewShell(); ImplSVData* pSVData = ImplGetSVData(); SvpSalInstance* pSalInstance = static_cast< SvpSalInstance* >(pSVData->mpDefInst); pSalInstance->setBitCountFormatMapping( 32, ::basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_RGBA ); VirtualDevice aDevice(0, (sal_uInt16)32); boost::shared_array< sal_uInt8 > aBuffer( pBuffer, NoDelete< sal_uInt8 >() ); aDevice.SetOutputSizePixelScaleOffsetAndBuffer( Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), aBuffer, true ); pViewShell->PaintTile(aDevice, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight); SvpSalVirtualDevice* pSalDev = static_cast< SvpSalVirtualDevice* >(aDevice.getSalVirtualDevice()); basebmp::BitmapDeviceSharedPtr pBmpDev = pSalDev->getBitmapDevice(); *pRowStride = pBmpDev->getScanlineStride(); } break; default: break; } Application::ReleaseSolarMutex(); } static void doc_getDocumentSize(LibreOfficeKitDocument* pThis, long* pWidth, long* pHeight) { LibLODocument_Impl* pDocument = static_cast(pThis); if (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT) { SwXTextDocument* pTxtDoc = dynamic_cast< SwXTextDocument* >( pDocument->mxComponent.get() ); SwDocShell* pDocShell = pTxtDoc->GetDocShell(); SwDoc* pDoc = pDocShell->GetDoc(); SwViewShell* pViewShell = pDoc->GetCurrentViewShell(); Size aDocumentSize = pViewShell->GetDocSize(); *pWidth = aDocumentSize.Width(); *pHeight = aDocumentSize.Height(); } else { pWidth = 0; pHeight = 0; } } static char* lo_getError (LibreOfficeKit *pThis) { LibLibreOffice_Impl* pLib = static_cast(pThis); OString aString = OUStringToOString(pLib->maLastExceptionMsg, RTL_TEXTENCODING_UTF8); char* pMemory = (char*) malloc(aString.getLength() + 1); strcpy(pMemory, aString.getStr()); return pMemory; } static void force_c_locale(void) { // force locale (and resource files loaded) to en-US OUString aLangISO("en-US"); LanguageTag aLocale(aLangISO); ResMgr::SetDefaultLocale(aLocale); SvtSysLocaleOptions aLocalOptions; aLocalOptions.SetLocaleConfigString(aLangISO); aLocalOptions.SetUILocaleConfigString(aLangISO); } static void aBasicErrorFunc(const OUString& rError, const OUString& rAction) { OStringBuffer aBuffer("Unexpected dialog: "); aBuffer.append(OUStringToOString(rAction, RTL_TEXTENCODING_ASCII_US)); aBuffer.append(" Error: "); aBuffer.append(OUStringToOString(rError, RTL_TEXTENCODING_ASCII_US)); fprintf(stderr, "Unexpected basic error dialog '%s'\n", aBuffer.getStr()); } static void initialize_uno(const OUString &aAppURL) { rtl::Bootstrap::setIniFilename( aAppURL + "/fundamentalrc" ); rtl::Bootstrap::set( "CONFIGURATION_LAYERS", "xcsxcu:${BRAND_BASE_DIR}/" LIBO_SHARE_FOLDER "/registry " "res:${BRAND_BASE_DIR}/" LIBO_SHARE_FOLDER "/registry " // "bundledext:${${BRAND_BASE_DIR}/" LIBO_ETC_FOLDER "/unorc:BUNDLED_EXTENSIONS_USER}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini " ); // "sharedext:${${BRAND_BASE_DIR}/" LIBO_ETC_FOLDER "/unorc:SHARED_EXTENSIONS_USER}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini " // "userext:${${BRAND_BASE_DIR}/" LIBO_ETC_FOLDER "/unorc:UNO_USER_PACKAGES_CACHE}/registry/com.sun.star.comp.deployment.configuration.PackageRegistryBackend/configmgr.ini " // "user:${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/bootstraprc:UserInstallation}/user/registrymodifications.xcu" ); xContext = cppu::defaultBootstrap_InitialComponentContext(); fprintf(stderr, "Uno initialized %d\n", xContext.is()); xFactory = xContext->getServiceManager(); xSFactory = uno::Reference(xFactory, uno::UNO_QUERY_THROW); comphelper::setProcessServiceFactory(xSFactory); // set UserInstallation to user profile dir in test/user-template // rtl::Bootstrap aDefaultVars; // aDefaultVars.set(OUString("UserInstallation"), aAppURL + "../registry" ); // configmgr setup ? } static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath) { (void) pThis; static bool bInitialized = false; if (bInitialized) return 1; if (!pAppPath) return 0; OUString aAppPath; if (pAppPath) { aAppPath = OUString(pAppPath, strlen(pAppPath), RTL_TEXTENCODING_UTF8); } else { ::osl::Module::getUrlFromAddress( reinterpret_cast< oslGenericFunction >(lo_initialize), aAppPath); } OUString aAppURL; if (osl::FileBase::getFileURLFromSystemPath(aAppPath, aAppURL) != osl::FileBase::E_None) return 0; try { osl_setCommandArgs(0, NULL); initialize_uno(aAppURL); force_c_locale(); // Force headless rtl::Bootstrap::set("SAL_USE_VCLPLUGIN", "svp"); InitVCL(); Application::EnableHeadlessMode(true); ErrorHandler::RegisterDisplay(aBasicErrorFunc); fprintf(stderr, "initialized\n"); bInitialized = true; } catch (css::uno::Exception& exception) { fprintf(stderr, "bootstrapping exception '%s'\n", OUStringToOString(exception.Message, RTL_TEXTENCODING_UTF8).getStr()); } return bInitialized; } SAL_DLLPUBLIC_EXPORT LibreOfficeKit *libreofficekit_hook(const char* install_path) { if (!gImpl) { fprintf(stderr, "create libreoffice object\n"); gImpl = new LibLibreOffice_Impl(); if (!lo_initialize(gImpl, install_path)) { lo_destroy(gImpl); } } return static_cast(gImpl); } static void lo_destroy(LibreOfficeKit *pThis) { LibLibreOffice_Impl* pLib = static_cast(pThis); delete pLib; gImpl = NULL; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */