1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 10 #include <sfx2/lokhelper.hxx> 11 #include <sal/types.h> 12 #include <svx/sdr/contact/viewcontact.hxx> 13 #include <svx/svdpage.hxx> 14 #include <svx/svdpagv.hxx> 15 #include <config_buildconfig.h> 16 #include <config_cairo_rgba.h> 17 #include <config_features.h> 18 19 #include <stdio.h> 20 #include <string.h> 21 #include <stdlib.h> 22 23 #ifdef IOS 24 #include <sys/mman.h> 25 #include <sys/stat.h> 26 #include <unicode/udata.h> 27 #include <unicode/ucnv.h> 28 #include <premac.h> 29 #import <Foundation/Foundation.h> 30 #import <CoreGraphics/CoreGraphics.h> 31 #include <postmac.h> 32 #endif 33 34 #undef HAVE_MALLOC_TRIM 35 36 #ifdef LINUX 37 #include <fcntl.h> 38 #if defined __GLIBC__ 39 # include <malloc.h> 40 # define HAVE_MALLOC_TRIM 41 #endif 42 #endif 43 44 #ifdef ANDROID 45 #include <osl/detail/android-bootstrap.h> 46 #endif 47 48 #ifdef EMSCRIPTEN 49 #include <osl/detail/emscripten-bootstrap.h> 50 #endif 51 52 #include <algorithm> 53 #include <memory> 54 #include <iostream> 55 #include <string_view> 56 #include <queue> 57 58 #include <boost/property_tree/json_parser.hpp> 59 #include <boost/algorithm/string.hpp> 60 61 #include <LibreOfficeKit/LibreOfficeKit.h> 62 #include <LibreOfficeKit/LibreOfficeKitEnums.h> 63 64 #include <sal/log.hxx> 65 #include <utility> 66 #include <vcl/errinf.hxx> 67 #include <vcl/lok.hxx> 68 #include <o3tl/any.hxx> 69 #include <o3tl/unit_conversion.hxx> 70 #include <o3tl/string_view.hxx> 71 #include <osl/file.hxx> 72 #include <osl/process.h> 73 #include <osl/thread.h> 74 #include <rtl/bootstrap.hxx> 75 #include <rtl/strbuf.hxx> 76 #include <rtl/uri.hxx> 77 #include <svl/zforlist.hxx> 78 #include <linguistic/misc.hxx> 79 #include <cppuhelper/bootstrap.hxx> 80 #include <comphelper/base64.hxx> 81 #include <comphelper/dispatchcommand.hxx> 82 #include <comphelper/lok.hxx> 83 #include <comphelper/processfactory.hxx> 84 #include <comphelper/string.hxx> 85 #include <comphelper/profilezone.hxx> 86 #include <comphelper/propertysequence.hxx> 87 #include <comphelper/propertyvalue.hxx> 88 #include <comphelper/scopeguard.hxx> 89 #include <comphelper/threadpool.hxx> 90 #include <comphelper/types.hxx> 91 #include <comphelper/servicehelper.hxx> 92 #include <comphelper/sequenceashashmap.hxx> 93 94 #include <com/sun/star/connection/XConnection.hpp> 95 #include <com/sun/star/document/MacroExecMode.hpp> 96 #include <com/sun/star/beans/XPropertySet.hpp> 97 #include <com/sun/star/container/XNameAccess.hpp> 98 #include <com/sun/star/document/XDocumentLanguages.hpp> 99 #include <com/sun/star/frame/Desktop.hpp> 100 #include <com/sun/star/frame/DispatchResultEvent.hpp> 101 #include <com/sun/star/frame/DispatchResultState.hpp> 102 #include <com/sun/star/frame/XDispatchProvider.hpp> 103 #include <com/sun/star/frame/XDispatchResultListener.hpp> 104 #include <com/sun/star/frame/XSynchronousDispatch.hpp> 105 #include <com/sun/star/frame/XStorable.hpp> 106 #include <com/sun/star/lang/Locale.hpp> 107 #include <com/sun/star/lang/XComponent.hpp> 108 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 109 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp> 110 #include <com/sun/star/util/URLTransformer.hpp> 111 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> 112 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> 113 #include <com/sun/star/datatransfer/XTransferable2.hpp> 114 #include <com/sun/star/text/TextContentAnchorType.hpp> 115 #include <com/sun/star/document/XRedlinesSupplier.hpp> 116 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> 117 #include <com/sun/star/bridge/BridgeFactory.hpp> 118 #include <com/sun/star/bridge/XBridgeFactory.hpp> 119 #include <com/sun/star/bridge/XBridge.hpp> 120 #include <com/sun/star/uno/XNamingService.hpp> 121 122 #include <com/sun/star/xml/crypto/SEInitializer.hpp> 123 #include <com/sun/star/xml/crypto/XSEInitializer.hpp> 124 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> 125 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp> 126 #include <com/sun/star/security/XCertificate.hpp> 127 128 #include <com/sun/star/linguistic2/DictionaryList.hpp> 129 #include <com/sun/star/linguistic2/LanguageGuessing.hpp> 130 #include <com/sun/star/linguistic2/LinguServiceManager.hpp> 131 #include <com/sun/star/linguistic2/XSpellChecker.hpp> 132 #include <com/sun/star/linguistic2/XProofreader.hpp> 133 #include <com/sun/star/i18n/LocaleCalendar2.hpp> 134 #include <com/sun/star/i18n/ScriptType.hpp> 135 #include <com/sun/star/lang/DisposedException.hpp> 136 #include <com/sun/star/view/XSelectionSupplier.hpp> 137 138 #include <editeng/flstitem.hxx> 139 #ifdef IOS 140 #include <sfx2/app.hxx> 141 #endif 142 #include <sfx2/objsh.hxx> 143 #include <sfx2/docfilt.hxx> 144 #include <sfx2/docfile.hxx> 145 #include <sfx2/viewsh.hxx> 146 #include <sfx2/viewfrm.hxx> 147 #include <sfx2/msgpool.hxx> 148 #include <sfx2/dispatch.hxx> 149 #include <sfx2/lokcomponenthelpers.hxx> 150 #include <sfx2/DocumentSigner.hxx> 151 #include <sfx2/sidebar/SidebarDockingWindow.hxx> 152 #include <sfx2/sidebar/SidebarController.hxx> 153 #include <svl/numformat.hxx> 154 #include <svx/dialmgr.hxx> 155 #include <svx/strings.hrc> 156 #include <svx/svdview.hxx> 157 #include <svx/svxids.hrc> 158 #include <svx/ucsubset.hxx> 159 #include <vcl/vclevent.hxx> 160 #include <vcl/GestureEventPan.hxx> 161 #include <vcl/svapp.hxx> 162 #include <unotools/resmgr.hxx> 163 #include <tools/fract.hxx> 164 #include <tools/json_writer.hxx> 165 #include <svtools/ctrltool.hxx> 166 #include <svtools/langtab.hxx> 167 #include <vcl/fontcharmap.hxx> 168 #ifdef IOS 169 #include <vcl/sysdata.hxx> 170 #endif 171 #include <vcl/virdev.hxx> 172 #include <vcl/ImageTree.hxx> 173 #include <vcl/ITiledRenderable.hxx> 174 #include <vcl/dialoghelper.hxx> 175 #ifdef _WIN32 176 #include <vcl/BitmapReadAccess.hxx> 177 #endif 178 #include <unicode/uchar.h> 179 #include <unotools/securityoptions.hxx> 180 #include <unotools/confignode.hxx> 181 #include <unotools/syslocaleoptions.hxx> 182 #include <unotools/mediadescriptor.hxx> 183 #include <unotools/pathoptions.hxx> 184 #include <unotools/tempfile.hxx> 185 #include <unotools/streamwrap.hxx> 186 #include <osl/module.hxx> 187 #include <comphelper/sequence.hxx> 188 #include <sfx2/sfxbasemodel.hxx> 189 #include <svl/undo.hxx> 190 #include <unotools/datetime.hxx> 191 #include <i18nlangtag/mslangid.hxx> 192 #include <i18nlangtag/languagetag.hxx> 193 #include <vcl/abstdlg.hxx> 194 #include <comphelper/diagnose_ex.hxx> 195 #include <vcl/uitest/uiobject.hxx> 196 #include <vcl/jsdialog/executor.hxx> 197 198 // Needed for getUndoManager() 199 #include <com/sun/star/document/XUndoManager.hpp> 200 #include <com/sun/star/document/XUndoManagerSupplier.hpp> 201 #include <com/sun/star/document/XLinkTargetSupplier.hpp> 202 #include <editeng/sizeitem.hxx> 203 #include <svx/rulritem.hxx> 204 #include <svx/pageitem.hxx> 205 206 #include <app.hxx> 207 208 #include "../app/cmdlineargs.hxx" 209 // We also need to hackily be able to start the main libreoffice thread: 210 #include "../app/sofficemain.h" 211 #include "../app/officeipcthread.hxx" 212 #include <lib/init.hxx> 213 214 #include "lokinteractionhandler.hxx" 215 #include "lokclipboard.hxx" 216 #include <officecfg/Office/Common.hxx> 217 #include <officecfg/Office/Impress.hxx> 218 #include <officecfg/Office/Linguistic.hxx> 219 #include <officecfg/Office/UI/ToolbarMode.hxx> 220 #include <unotools/optionsdlg.hxx> 221 #include <svl/ctloptions.hxx> 222 #include <svtools/colorcfg.hxx> 223 #include <svtools/miscopt.hxx> 224 #include <unotools/cmdoptions.hxx> 225 #include <unotools/lingucfg.hxx> 226 #include <unotools/moduleoptions.hxx> 227 #include <unotools/searchopt.hxx> 228 #include <unotools/useroptions.hxx> 229 #include <unotools/viewoptions.hxx> 230 #include <vcl/settings.hxx> 231 232 #include <officecfg/Setup.hxx> 233 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp> 234 #include <svtools/acceleratorexecute.hxx> 235 236 #include <tools/hostfilter.hxx> 237 238 using namespace css; 239 using namespace vcl; 240 using namespace desktop; 241 using namespace utl; 242 using namespace bridge; 243 using namespace uno; 244 using namespace lang; 245 246 using LanguageToolCfg = officecfg::Office::Linguistic::GrammarChecking::LanguageTool; 247 248 static LibLibreOffice_Impl *gImpl = nullptr; 249 static bool lok_preinit_2_called = false; 250 static std::weak_ptr< LibreOfficeKitClass > gOfficeClass; 251 static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass; 252 253 static void SetLastExceptionMsg(const OUString& s = OUString()) 254 { 255 SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'"); 256 if (gImpl) 257 gImpl->maLastExceptionMsg = s; 258 } 259 260 namespace { 261 262 struct ExtensionMap 263 { 264 std::string_view extn; 265 OUString filterName; 266 }; 267 268 class TraceEventDumper : public AutoTimer 269 { 270 static const int dumpTimeoutMS = 5000; 271 272 public: 273 TraceEventDumper() : AutoTimer( "Trace Event dumper" ) 274 { 275 SetTimeout(dumpTimeoutMS); 276 Start(); 277 } 278 279 virtual void Invoke() override 280 { 281 flushRecordings(); 282 } 283 284 static void flushRecordings() 285 { 286 const css::uno::Sequence<OUString> aEvents = 287 comphelper::TraceEvent::getRecordingAndClear(); 288 OStringBuffer aOutput; 289 for (const auto &s : aEvents) 290 { 291 aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8) 292 + "\n"); 293 } 294 if (aOutput.getLength() > 0) 295 { 296 OString aChunk = aOutput.makeStringAndClear(); 297 if (gImpl && gImpl->mpCallback) 298 gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData); 299 } 300 } 301 }; 302 303 TraceEventDumper *traceEventDumper = nullptr; 304 305 constexpr ExtensionMap aWriterExtensionMap[] = 306 { 307 { "doc", u"MS Word 97"_ustr }, 308 { "docm", u"MS Word 2007 XML VBA"_ustr }, 309 { "docx", u"MS Word 2007 XML"_ustr }, 310 { "fodt", u"OpenDocument Text Flat XML"_ustr }, 311 { "html", u"HTML (StarWriter)"_ustr }, 312 { "odt", u"writer8"_ustr }, 313 { "ott", u"writer8_template"_ustr }, 314 { "pdf", u"writer_pdf_Export"_ustr }, 315 { "epub", u"EPUB"_ustr }, 316 { "rtf", u"Rich Text Format"_ustr }, 317 { "txt", u"Text"_ustr }, 318 { "xhtml", u"XHTML Writer File"_ustr }, 319 { "png", u"writer_png_Export"_ustr }, 320 { "xml", u"writer_indexing_export"_ustr }, 321 }; 322 323 constexpr ExtensionMap aCalcExtensionMap[] = 324 { 325 { "csv", u"Text - txt - csv (StarCalc)"_ustr }, 326 { "fods", u"OpenDocument Spreadsheet Flat XML"_ustr }, 327 { "html", u"HTML (StarCalc)"_ustr }, 328 { "ods", u"calc8"_ustr }, 329 { "ots", u"calc8_template"_ustr }, 330 { "pdf", u"calc_pdf_Export"_ustr }, 331 { "xhtml", u"XHTML Calc File"_ustr }, 332 { "xls", u"MS Excel 97"_ustr }, 333 { "xlsm", u"Calc MS Excel 2007 VBA XML"_ustr }, 334 { "xlsx", u"Calc MS Excel 2007 XML"_ustr }, 335 { "png", u"calc_png_Export"_ustr }, 336 }; 337 338 constexpr ExtensionMap aImpressExtensionMap[] = 339 { 340 { "fodp", u"OpenDocument Presentation Flat XML"_ustr }, 341 { "html", u"impress_html_Export"_ustr }, 342 { "odg", u"impress8_draw"_ustr }, 343 { "odp", u"impress8"_ustr }, 344 { "otp", u"impress8_template"_ustr }, 345 { "pdf", u"impress_pdf_Export"_ustr }, 346 { "potm", u"Impress MS PowerPoint 2007 XML Template"_ustr }, 347 { "pot", u"MS PowerPoint 97 Vorlage"_ustr }, 348 { "pptm", u"Impress MS PowerPoint 2007 XML VBA"_ustr }, 349 { "pptx", u"Impress MS PowerPoint 2007 XML"_ustr }, 350 { "pps", u"MS PowerPoint 97 Autoplay"_ustr }, 351 { "ppt", u"MS PowerPoint 97"_ustr }, 352 { "svg", u"impress_svg_Export"_ustr }, 353 { "xhtml", u"XHTML Impress File"_ustr }, 354 { "png", u"impress_png_Export"_ustr }, 355 }; 356 357 constexpr ExtensionMap aDrawExtensionMap[] = 358 { 359 { "fodg", u"draw_ODG_FlatXML"_ustr }, 360 { "html", u"draw_html_Export"_ustr }, 361 { "odg", u"draw8"_ustr }, 362 { "pdf", u"draw_pdf_Export"_ustr }, 363 { "svg", u"draw_svg_Export"_ustr }, 364 { "xhtml", u"XHTML Draw File"_ustr }, 365 { "png", u"draw_png_Export"_ustr }, 366 }; 367 368 OUString getUString(const char* pString) 369 { 370 if (pString == nullptr) 371 return OUString(); 372 373 return OStringToOUString(pString, RTL_TEXTENCODING_UTF8); 374 } 375 376 // Tolerate embedded \0s etc. 377 char *convertOString(const OString &rStr) 378 { 379 char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1)); 380 assert(pMemory); // don't tolerate failed allocations. 381 memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1); 382 return pMemory; 383 } 384 385 char *convertOUString(std::u16string_view aStr) 386 { 387 return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8)); 388 } 389 390 /// Try to convert a relative URL to an absolute one, unless it already looks like a URL. 391 OUString getAbsoluteURL(const char* pURL) 392 { 393 OUString aURL(getUString(pURL)); 394 if (aURL.isEmpty()) 395 return aURL; 396 397 // convert relative paths to absolute ones 398 OUString aWorkingDir; 399 osl_getProcessWorkingDir(&aWorkingDir.pData); 400 if (!aWorkingDir.endsWith("/")) 401 aWorkingDir += "/"; 402 403 try 404 { 405 return rtl::Uri::convertRelToAbs(aWorkingDir, aURL); 406 } 407 catch (const rtl::MalformedUriException &) 408 { 409 } 410 411 return OUString(); 412 } 413 414 } // unnamed namespace 415 416 std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON) 417 { 418 std::vector<beans::PropertyValue> aArguments; 419 if (pJSON && pJSON[0] != '\0') 420 { 421 aArguments = comphelper::JsonToPropertyValues(pJSON); 422 } 423 return aArguments; 424 } 425 426 static void extractLinks(const uno::Reference< container::XNameAccess >& xLinks, bool subcontent, tools::JsonWriter& aJson) 427 { 428 for (const OUString& aLink : xLinks->getElementNames()) 429 { 430 uno::Any aAny; 431 432 try 433 { 434 aAny = xLinks->getByName( aLink ); 435 } 436 catch(const uno::Exception&) 437 { 438 // if the name of the target was invalid (like empty headings) 439 // no object can be provided 440 continue; 441 } 442 443 uno::Reference< beans::XPropertySet > xTarget; 444 if( aAny >>= xTarget ) 445 { 446 try 447 { 448 // get name to display 449 aAny = xTarget->getPropertyValue(u"LinkDisplayName"_ustr); 450 OUString aStrDisplayname; 451 aAny >>= aStrDisplayname; 452 453 if (subcontent) 454 { 455 aJson.put(aStrDisplayname, aLink); 456 } 457 else 458 { 459 uno::Reference<lang::XServiceInfo> xSI(xTarget, uno::UNO_QUERY_THROW); 460 if (xSI->supportsService(u"com.sun.star.document.LinkTarget"_ustr)) 461 { 462 aJson.put(aStrDisplayname, aLink); 463 continue; 464 } 465 else 466 { 467 auto aNode = aJson.startNode( 468 OUStringToOString(aStrDisplayname, RTL_TEXTENCODING_UTF8)); 469 470 uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY ); 471 if( xLTS.is() ) 472 extractLinks(xLTS->getLinks(), true, aJson); 473 } 474 } 475 } 476 catch(...) 477 { 478 SAL_WARN("lok", "extractLinks: Exception"); 479 } 480 } 481 } 482 } 483 484 static void unoAnyToJson(tools::JsonWriter& rJson, std::string_view pNodeName, const uno::Any& anyItem) 485 { 486 auto aNode = rJson.startNode(pNodeName); 487 OUString aType = anyItem.getValueTypeName(); 488 rJson.put("type", aType); 489 490 if (aType == "string") 491 rJson.put("value", anyItem.get<OUString>()); 492 else if (aType == "unsigned long") 493 rJson.put("value", OString::number(anyItem.get<sal_uInt32>())); 494 else if (aType == "long") 495 rJson.put("value", OString::number(anyItem.get<sal_Int32>())); 496 else if (aType == "[]any") 497 { 498 uno::Sequence<uno::Any> aSeq; 499 if (anyItem >>= aSeq) 500 { 501 auto valueNode = rJson.startNode("value"); 502 503 for (auto i = 0; i < aSeq.getLength(); ++i) 504 { 505 unoAnyToJson(rJson, OString::number(i), aSeq[i]); 506 } 507 } 508 } 509 } 510 511 static int lcl_getViewId(std::string_view payload); 512 513 namespace desktop { 514 515 RectangleAndPart RectangleAndPart::Create(const OString& rPayload) 516 { 517 RectangleAndPart aRet; 518 if (rPayload.startsWith("EMPTY")) // payload starts with "EMPTY" 519 { 520 aRet.m_aRectangle = tools::Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips); 521 if (comphelper::LibreOfficeKit::isPartInInvalidation()) 522 { 523 int nSeparatorPos = rPayload.indexOf(',', 6); 524 bool bHasMode = nSeparatorPos > 0; 525 if (bHasMode) 526 { 527 aRet.m_nPart = o3tl::toInt32(rPayload.subView(6, nSeparatorPos - 6)); 528 assert(rPayload.getLength() > nSeparatorPos); 529 aRet.m_nMode = o3tl::toInt32(rPayload.subView(nSeparatorPos + 1)); 530 } 531 else 532 { 533 aRet.m_nPart = o3tl::toInt32(rPayload.subView(6)); 534 aRet.m_nMode = 0; 535 } 536 } 537 538 return aRet; 539 } 540 541 // Read '<left>, <top>, <width>, <height>[, <part>, <mode>]'. C++ streams are simpler but slower. 542 const char* pos = rPayload.getStr(); 543 const char* end = rPayload.getStr() + rPayload.getLength(); 544 tools::Long nLeft = rtl_str_toInt64_WithLength(pos, 10, end - pos); 545 while (pos < end && *pos != ',') 546 ++pos; 547 if (pos < end) 548 ++pos; 549 assert(pos < end); 550 tools::Long nTop = rtl_str_toInt64_WithLength(pos, 10, end - pos); 551 while (pos < end && *pos != ',') 552 ++pos; 553 if (pos < end) 554 ++pos; 555 assert(pos < end); 556 tools::Long nWidth = rtl_str_toInt64_WithLength(pos, 10, end - pos); 557 while (pos < end && *pos != ',') 558 ++pos; 559 if (pos < end) 560 ++pos; 561 assert(pos < end); 562 tools::Long nHeight = rtl_str_toInt64_WithLength(pos, 10, end - pos); 563 tools::Long nPart = INT_MIN; 564 tools::Long nMode = 0; 565 if (comphelper::LibreOfficeKit::isPartInInvalidation()) 566 { 567 while (pos < end && *pos != ',') 568 ++pos; 569 if (pos < end) 570 ++pos; 571 assert(pos < end); 572 nPart = rtl_str_toInt64_WithLength(pos, 10, end - pos); 573 574 while (pos < end && *pos != ',') 575 ++pos; 576 if (pos < end) 577 { 578 ++pos; 579 assert(pos < end); 580 nMode = rtl_str_toInt64_WithLength(pos, 10, end - pos); 581 } 582 } 583 584 aRet.m_aRectangle = SanitizedRectangle(nLeft, nTop, nWidth, nHeight); 585 aRet.m_nPart = nPart; 586 aRet.m_nMode = nMode; 587 return aRet; 588 } 589 590 tools::Rectangle RectangleAndPart::SanitizedRectangle(tools::Long nLeft, tools::Long nTop, tools::Long nWidth, tools::Long nHeight) 591 { 592 if (nWidth <= 0 || nHeight <= 0) 593 return tools::Rectangle(); 594 595 // The top-left corner starts at (0, 0). 596 // Anything negative is invalid. 597 if (nLeft < 0) 598 { 599 nWidth += nLeft; 600 nLeft = 0; 601 } 602 603 if (nTop < 0) 604 { 605 nHeight += nTop; 606 nTop = 0; 607 } 608 609 if (nWidth > 0 && nHeight > 0) 610 return tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight); 611 // Else set empty rect. 612 return tools::Rectangle(); 613 } 614 615 tools::Rectangle RectangleAndPart::SanitizedRectangle(const tools::Rectangle& rect) 616 { 617 return SanitizedRectangle(rect.Left(), rect.Top(), rect.getOpenWidth(), rect.getOpenHeight()); 618 } 619 620 const OString& CallbackFlushHandler::CallbackData::getPayload() const 621 { 622 if(PayloadString.isEmpty()) 623 { 624 // Do to-string conversion on demand, as many calls will get dropped without 625 // needing the string. 626 if(PayloadObject.which() == 1) 627 PayloadString = getRectangleAndPart().toString(); 628 } 629 return PayloadString; 630 } 631 632 void CallbackFlushHandler::CallbackData::updateRectangleAndPart(const RectangleAndPart& rRectAndPart) 633 { 634 PayloadObject = rRectAndPart; 635 PayloadString.clear(); // will be set on demand if needed 636 } 637 638 const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const 639 { 640 // TODO: In case of unittests, they do not pass invalidations in binary but as text messages. 641 // LO core should preferably always pass binary for performance. 642 if(PayloadObject.which() != 1) 643 PayloadObject = RectangleAndPart::Create(PayloadString); 644 return boost::get<RectangleAndPart>(PayloadObject); 645 } 646 647 boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload) 648 { 649 boost::property_tree::ptree aTree; 650 std::stringstream aStream(payload); 651 boost::property_tree::read_json(aStream, aTree); 652 653 // Let boost normalize the payload so it always matches the cache. 654 setJson(aTree); 655 656 // Return reference to the cached object. 657 return boost::get<boost::property_tree::ptree>(PayloadObject); 658 } 659 660 void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree) 661 { 662 std::stringstream aJSONStream; 663 constexpr bool bPretty = false; // Don't waste time and bloat logs. 664 boost::property_tree::write_json(aJSONStream, rTree, bPretty); 665 PayloadString = OString(o3tl::trim(aJSONStream.str())); 666 667 PayloadObject = rTree; 668 } 669 670 const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const 671 { 672 assert(PayloadObject.which() == 2); 673 return boost::get<boost::property_tree::ptree>(PayloadObject); 674 } 675 676 int CallbackFlushHandler::CallbackData::getViewId() const 677 { 678 if (isCached()) 679 { 680 assert(PayloadObject.which() == 3); 681 return boost::get<int>(PayloadObject); 682 } 683 return lcl_getViewId(getPayload()); 684 } 685 686 bool CallbackFlushHandler::CallbackData::validate() const 687 { 688 switch (PayloadObject.which()) 689 { 690 // Not cached. 691 case 0: 692 return true; 693 694 // RectangleAndPart. 695 case 1: 696 return getRectangleAndPart().toString().getStr() == getPayload(); 697 698 // Json. 699 case 2: 700 { 701 std::stringstream aJSONStream; 702 boost::property_tree::write_json(aJSONStream, getJson(), false); 703 const std::string aExpected = boost::trim_copy(aJSONStream.str()); 704 return getPayload() == std::string_view(aExpected); 705 } 706 707 // View id. 708 case 3: 709 return getViewId() == lcl_getViewId( getPayload()); 710 711 default: 712 assert(!"Unknown variant type; please add an entry to validate."); 713 } 714 715 return false; 716 } 717 718 } // namespace desktop 719 720 static bool lcl_isViewCallbackType(const int type) 721 { 722 switch (type) 723 { 724 case LOK_CALLBACK_CELL_VIEW_CURSOR: 725 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: 726 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: 727 case LOK_CALLBACK_TEXT_VIEW_SELECTION: 728 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: 729 return true; 730 731 default: 732 return false; 733 } 734 } 735 736 static bool isUpdatedType(int type) 737 { 738 switch (type) 739 { 740 case LOK_CALLBACK_TEXT_SELECTION: 741 case LOK_CALLBACK_TEXT_SELECTION_START: 742 case LOK_CALLBACK_TEXT_SELECTION_END: 743 return true; 744 default: 745 return false; 746 } 747 } 748 749 static bool isUpdatedTypePerViewId(int type) 750 { 751 switch (type) 752 { 753 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: 754 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: 755 case LOK_CALLBACK_TEXT_VIEW_SELECTION: 756 return true; 757 default: 758 return false; 759 } 760 } 761 762 static int lcl_getViewId(std::string_view payload) 763 { 764 // this is a cheap way how to get the viewId from a JSON message; proper 765 // parsing is terribly expensive, and we just need the viewId here 766 size_t viewIdPos = payload.find("viewId"); 767 if (viewIdPos == std::string::npos) 768 return 0; 769 770 size_t numberPos = payload.find(":", viewIdPos + 6); 771 if (numberPos == std::string::npos) 772 return 0; 773 774 for (++numberPos; numberPos < payload.length(); ++numberPos) 775 { 776 if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9')) 777 break; 778 } 779 780 if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9') 781 return o3tl::toInt32(payload.substr(numberPos)); 782 783 return 0; 784 } 785 786 namespace { 787 788 std::string extractCertificate(const std::string & certificate) 789 { 790 static constexpr std::string_view header("-----BEGIN CERTIFICATE-----"); 791 static constexpr std::string_view footer("-----END CERTIFICATE-----"); 792 793 std::string result; 794 795 size_t pos1 = certificate.find(header); 796 if (pos1 == std::string::npos) 797 return result; 798 799 size_t pos2 = certificate.find(footer, pos1 + 1); 800 if (pos2 == std::string::npos) 801 return result; 802 803 pos1 = pos1 + header.length(); 804 pos2 = pos2 - pos1; 805 806 return certificate.substr(pos1, pos2); 807 } 808 809 std::string extractPrivateKey(const std::string & privateKey) 810 { 811 static constexpr std::string_view header("-----BEGIN PRIVATE KEY-----"); 812 static constexpr std::string_view footer("-----END PRIVATE KEY-----"); 813 814 std::string result; 815 816 size_t pos1 = privateKey.find(header); 817 if (pos1 == std::string::npos) 818 return result; 819 820 size_t pos2 = privateKey.find(footer, pos1 + 1); 821 if (pos2 == std::string::npos) 822 return result; 823 824 pos1 = pos1 + header.length(); 825 pos2 = pos2 - pos1; 826 827 return privateKey.substr(pos1, pos2); 828 } 829 830 OUString lcl_getCurrentDocumentMimeType(const LibLODocument_Impl* pDocument) 831 { 832 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 833 if (!pBaseModel) 834 return ""; 835 836 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 837 if (!pObjectShell) 838 return ""; 839 840 SfxMedium* pMedium = pObjectShell->GetMedium(); 841 if (!pMedium) 842 return ""; 843 844 auto pFilter = pMedium->GetFilter(); 845 if (!pFilter) 846 return ""; 847 848 return pFilter->GetMimeType(); 849 } 850 851 // Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation 852 css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame ) 853 { 854 const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController(); 855 if ( xController.is() ) 856 { 857 const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel(); 858 if ( xModel.is() ) 859 { 860 const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW ); 861 return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW ); 862 } 863 } 864 865 return css::uno::Reference< css::document::XUndoManager > (); 866 } 867 868 // Adjusts page margins for Writer doc. Needed by ToggleOrientation 869 void ExecuteMarginLRChange( 870 const tools::Long nPageLeftMargin, 871 const tools::Long nPageRightMargin, 872 SvxLongLRSpaceItem* pPageLRMarginItem) 873 { 874 pPageLRMarginItem->SetLeft( nPageLeftMargin ); 875 pPageLRMarginItem->SetRight( nPageRightMargin ); 876 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE, 877 SfxCallMode::RECORD, { pPageLRMarginItem }); 878 } 879 880 // Adjusts page margins for Writer doc. Needed by ToggleOrientation 881 void ExecuteMarginULChange( 882 const tools::Long nPageTopMargin, 883 const tools::Long nPageBottomMargin, 884 SvxLongULSpaceItem* pPageULMarginItem) 885 { 886 pPageULMarginItem->SetUpper( nPageTopMargin ); 887 pPageULMarginItem->SetLower( nPageBottomMargin ); 888 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE, 889 SfxCallMode::RECORD, { pPageULMarginItem }); 890 } 891 892 // Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation 893 void ExecuteOrientationChange() 894 { 895 SfxViewFrame* pViewFrm = SfxViewFrame::Current(); 896 if (!pViewFrm) 897 return; 898 899 std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE)); 900 901 // 1mm in twips rounded 902 // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx 903 constexpr tools::Long MINBODY = o3tl::toTwips(1, o3tl::Length::mm); 904 905 css::uno::Reference< css::document::XUndoManager > mxUndoManager( 906 getUndoManager( pViewFrm->GetFrame().GetFrameInterface() ) ); 907 908 if ( mxUndoManager.is() ) 909 mxUndoManager->enterUndoContext( "" ); 910 911 SfxPoolItemHolder aResult; 912 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, aResult); 913 std::unique_ptr<SvxSizeItem> pPageSizeItem(static_cast<const SvxSizeItem*>(aResult.getItem())->Clone()); 914 915 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, aResult); 916 std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(static_cast<const SvxLongLRSpaceItem*>(aResult.getItem())->Clone()); 917 918 pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, aResult); 919 std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(static_cast<const SvxLongULSpaceItem*>(aResult.getItem())->Clone()); 920 921 { 922 bool bIsLandscape = false; 923 if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height()) 924 bIsLandscape = true; 925 926 // toggle page orientation 927 pPageItem->SetLandscape(!bIsLandscape); 928 929 930 // swap the width and height of the page size 931 const tools::Long nRotatedWidth = pPageSizeItem->GetSize().Height(); 932 const tools::Long nRotatedHeight = pPageSizeItem->GetSize().Width(); 933 pPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight)); 934 935 936 // apply changed attributes 937 if (SfxViewShell::Current()) 938 { 939 SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, 940 SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() }); 941 } 942 } 943 944 945 // check, if margin values still fit to the changed page size. 946 // if not, adjust margin values 947 { 948 const tools::Long nML = pPageLRMarginItem->GetLeft(); 949 const tools::Long nMR = pPageLRMarginItem->GetRight(); 950 const tools::Long nTmpPW = nML + nMR + MINBODY; 951 952 const tools::Long nPW = pPageSizeItem->GetSize().Width(); 953 954 if ( nTmpPW > nPW ) 955 { 956 if ( nML <= nMR ) 957 { 958 ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() ); 959 } 960 else 961 { 962 ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() ); 963 } 964 } 965 966 const tools::Long nMT = pPageULMarginItem->GetUpper(); 967 const tools::Long nMB = pPageULMarginItem->GetLower(); 968 const tools::Long nTmpPH = nMT + nMB + MINBODY; 969 970 const tools::Long nPH = pPageSizeItem->GetSize().Height(); 971 972 if ( nTmpPH > nPH ) 973 { 974 if ( nMT <= nMB ) 975 { 976 ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() ); 977 } 978 else 979 { 980 ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() ); 981 } 982 } 983 } 984 985 if ( mxUndoManager.is() ) 986 mxUndoManager->leaveUndoContext(); 987 } 988 989 void setupSidebar(std::u16string_view sidebarDeckId = u"") 990 { 991 SfxViewShell* pViewShell = SfxViewShell::Current(); 992 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr; 993 if (pViewFrame) 994 { 995 if (!pViewFrame->GetChildWindow(SID_SIDEBAR)) 996 pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */); 997 998 pViewFrame->ShowChildWindow(SID_SIDEBAR, true); 999 1000 // Force synchronous population of panels 1001 SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR); 1002 if (!pChild) 1003 return; 1004 1005 auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow()); 1006 if (!pDockingWin) 1007 return; 1008 1009 pViewFrame->ShowChildWindow( SID_SIDEBAR ); 1010 1011 const rtl::Reference<sfx2::sidebar::SidebarController>& xController 1012 = pDockingWin->GetOrCreateSidebarController(); 1013 1014 xController->FadeIn(); 1015 xController->RequestOpenDeck(); 1016 1017 if (!sidebarDeckId.empty()) 1018 { 1019 xController->SwitchToDeck(sidebarDeckId); 1020 } 1021 else 1022 { 1023 xController->SwitchToDefaultDeck(); 1024 } 1025 1026 pDockingWin->SyncUpdate(); 1027 } 1028 else 1029 SetLastExceptionMsg(u"No view shell or sidebar"_ustr); 1030 } 1031 1032 void hideSidebar() 1033 { 1034 SfxViewShell* pViewShell = SfxViewShell::Current(); 1035 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr; 1036 if (pViewFrame) 1037 pViewFrame->SetChildWindow(SID_SIDEBAR, false , false ); 1038 else 1039 SetLastExceptionMsg(u"No view shell or sidebar"_ustr); 1040 } 1041 1042 } // end anonymous namespace 1043 1044 // Could be anonymous in principle, but for the unit testing purposes, we 1045 // declare it in init.hxx. 1046 OUString desktop::extractParameter(OUString& rOptions, std::u16string_view rName) 1047 { 1048 OUString aValue; 1049 1050 OUString aNameEquals(OUString::Concat(rName) + "="); 1051 OUString aCommaNameEquals(OUString::Concat(",") + rName + "="); 1052 1053 int nIndex = -1; 1054 if (rOptions.startsWith(aNameEquals)) 1055 { 1056 size_t nLen = aNameEquals.getLength(); 1057 int nComma = rOptions.indexOf(",", nLen); 1058 if (nComma >= 0) 1059 { 1060 aValue = rOptions.copy(nLen, nComma - nLen); 1061 rOptions = rOptions.copy(nComma + 1); 1062 } 1063 else 1064 { 1065 aValue = rOptions.copy(nLen); 1066 rOptions.clear(); 1067 } 1068 } 1069 else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0) 1070 { 1071 size_t nLen = aCommaNameEquals.getLength(); 1072 int nComma = rOptions.indexOf(",", nIndex + nLen); 1073 if (nComma >= 0) 1074 { 1075 aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen); 1076 rOptions = OUString::Concat(rOptions.subView(0, nIndex)) + rOptions.subView(nComma); 1077 } 1078 else 1079 { 1080 aValue = rOptions.copy(nIndex + nLen); 1081 rOptions = rOptions.copy(0, nIndex); 1082 } 1083 } 1084 1085 return aValue; 1086 } 1087 1088 extern "C" 1089 { 1090 1091 static void doc_destroy(LibreOfficeKitDocument* pThis); 1092 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions); 1093 static int doc_getDocumentType(LibreOfficeKitDocument* pThis); 1094 static int doc_getParts(LibreOfficeKitDocument* pThis); 1095 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis); 1096 static int doc_getPart(LibreOfficeKitDocument* pThis); 1097 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart); 1098 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect); 1099 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate); 1100 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart); 1101 static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode); 1102 static int doc_getEditMode(LibreOfficeKitDocument* pThis); 1103 static void doc_paintTile(LibreOfficeKitDocument* pThis, 1104 unsigned char* pBuffer, 1105 const int nCanvasWidth, const int nCanvasHeight, 1106 const int nTilePosX, const int nTilePosY, 1107 const int nTileWidth, const int nTileHeight); 1108 static void doc_paintPartTile(LibreOfficeKitDocument* pThis, 1109 unsigned char* pBuffer, 1110 const int nPart, 1111 const int nMode, 1112 const int nCanvasWidth, const int nCanvasHeight, 1113 const int nTilePosX, const int nTilePosY, 1114 const int nTileWidth, const int nTileHeight); 1115 static int doc_getTileMode(LibreOfficeKitDocument* pThis); 1116 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis, 1117 long* pWidth, 1118 long* pHeight); 1119 static void doc_getDataArea(LibreOfficeKitDocument* pThis, 1120 long nTab, 1121 long* pCol, 1122 long* pRow); 1123 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis, 1124 const char* pArguments); 1125 1126 static void doc_registerCallback(LibreOfficeKitDocument* pThis, 1127 LibreOfficeKitCallback pCallback, 1128 void* pData); 1129 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, 1130 int nType, 1131 int nCharCode, 1132 int nKeyCode); 1133 static void doc_setBlockedCommandList(LibreOfficeKitDocument* pThis, 1134 int nViewId, 1135 const char* blockedCommandList); 1136 1137 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, 1138 unsigned nWindowId, 1139 int nType, 1140 const char* pText); 1141 1142 static char* doc_hyperlinkInfoAtPosition(LibreOfficeKitDocument *pThis, int x, int y); 1143 1144 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, 1145 unsigned nLOKWindowId, 1146 int nCharBefore, 1147 int nCharAfter); 1148 static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis, 1149 unsigned long long int nLOKWindowId, 1150 const char* pArguments); 1151 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis, 1152 unsigned nLOKWindowId, 1153 int nType, 1154 int nCharCode, 1155 int nKeyCode); 1156 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis, 1157 int nType, 1158 int nX, 1159 int nY, 1160 int nCount, 1161 int nButtons, 1162 int nModifier); 1163 static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis, 1164 unsigned nLOKWindowId, 1165 int nType, 1166 int nX, 1167 int nY, 1168 int nCount, 1169 int nButtons, 1170 int nModifier); 1171 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis, 1172 unsigned nLOKWindowId, 1173 const char* pType, 1174 int nX, 1175 int nY, 1176 int nOffset); 1177 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, 1178 const char* pCommand, 1179 const char* pArguments, 1180 bool bNotifyWhenFinished); 1181 static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis, 1182 unsigned nLOKWindowId, 1183 bool swap, 1184 int nX, 1185 int nY); 1186 static void doc_setTextSelection (LibreOfficeKitDocument* pThis, 1187 int nType, 1188 int nX, 1189 int nY); 1190 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, 1191 const char* pMimeType, 1192 char** pUsedMimeType); 1193 static int doc_getSelectionType(LibreOfficeKitDocument* pThis); 1194 static int doc_getSelectionTypeAndText(LibreOfficeKitDocument* pThis, 1195 const char* pMimeType, 1196 char** pText, 1197 char** pUsedMimeType); 1198 static int doc_getClipboard (LibreOfficeKitDocument* pThis, 1199 const char **pMimeTypes, 1200 size_t *pOutCount, 1201 char ***pOutMimeTypes, 1202 size_t **pOutSizes, 1203 char ***pOutStreams); 1204 static int doc_setClipboard (LibreOfficeKitDocument* pThis, 1205 const size_t nInCount, 1206 const char **pInMimeTypes, 1207 const size_t *pInSizes, 1208 const char **pInStreams); 1209 static bool doc_paste(LibreOfficeKitDocument* pThis, 1210 const char* pMimeType, 1211 const char* pData, 1212 size_t nSize); 1213 static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis, 1214 int nType, 1215 int nX, 1216 int nY); 1217 static void doc_resetSelection (LibreOfficeKitDocument* pThis); 1218 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand); 1219 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, 1220 int nTilePixelWidth, 1221 int nTilePixelHeight, 1222 int nTileTwipWidth, 1223 int nTileTwipHeight); 1224 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight); 1225 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden); 1226 static int doc_createView(LibreOfficeKitDocument* pThis); 1227 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions); 1228 static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId); 1229 static void doc_setView(LibreOfficeKitDocument* pThis, int nId); 1230 static int doc_getView(LibreOfficeKitDocument* pThis); 1231 static int doc_getViewsCount(LibreOfficeKitDocument* pThis); 1232 static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize); 1233 static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language); 1234 static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis, 1235 const char *pFontName, 1236 const char *pChar, 1237 int* pFontWidth, 1238 int* pFontHeight, 1239 int pOrientation); 1240 static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis, 1241 const char *pFontName, 1242 const char *pChar, 1243 int* pFontWidth, 1244 int* pFontHeight); 1245 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart); 1246 1247 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer, 1248 const int nX, const int nY, 1249 const int nWidth, const int nHeight); 1250 1251 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer, 1252 const int nX, const int nY, 1253 const int nWidth, const int nHeight, 1254 const double fDPIScale); 1255 1256 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer, 1257 const int nX, const int nY, 1258 const int nWidth, const int nHeight, 1259 const double fDPIScale, int viewId); 1260 1261 static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned 1262 nLOKWindowId, int nAction, const char* pData); 1263 1264 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart); 1265 1266 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis, 1267 const unsigned char* pCertificateBinary, 1268 const int nCertificateBinarySize, 1269 const unsigned char* pPrivateKeyBinary, 1270 const int nPrivateKeyBinarySize); 1271 1272 static bool doc_addCertificate(LibreOfficeKitDocument* pThis, 1273 const unsigned char* pCertificateBinary, 1274 const int nCertificateBinarySize); 1275 1276 static int doc_getSignatureState(LibreOfficeKitDocument* pThis); 1277 1278 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput); 1279 1280 static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, 1281 const int nWidth, const int nHeight); 1282 1283 static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char*); 1284 1285 1286 static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis, 1287 const char* pArguments); 1288 1289 static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis, 1290 const char* pSearchResult, unsigned char** pBitmapBuffer, 1291 int* pWidth, int* pHeight, size_t* pByteSize); 1292 1293 static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments); 1294 1295 static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const char* timezone); 1296 1297 static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, bool bEnabled); 1298 1299 static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis); 1300 1301 static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis); 1302 } // extern "C" 1303 1304 namespace { 1305 ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis) 1306 { 1307 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 1308 return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get()); 1309 } 1310 1311 #ifndef IOS 1312 1313 /* 1314 * Unfortunately clipboard creation using UNO is insanely baroque. 1315 * we also need to ensure that this works for the first view which 1316 * has no clear 'createView' called for it (unfortunately). 1317 */ 1318 rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis) 1319 { 1320 ITiledRenderable* pDoc = getTiledRenderable(pThis); 1321 rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView()); 1322 1323 SAL_INFO("lok", "Set to clipboard for view " << xClip.get()); 1324 // FIXME: using a hammer here - should not be necessary if all tests used createView. 1325 pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY)); 1326 1327 return xClip; 1328 } 1329 1330 #endif 1331 1332 const vcl::Font* FindFont(std::u16string_view rFontName) 1333 { 1334 SfxObjectShell* pDocSh = SfxObjectShell::Current(); 1335 if (!pDocSh) 1336 return nullptr; 1337 const SvxFontListItem* pFonts 1338 = static_cast<const SvxFontListItem*>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST)); 1339 const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr; 1340 if (pList && !rFontName.empty()) 1341 if (sal_Handle hMetric = pList->GetFirstFontMetric(rFontName)) 1342 return &FontList::GetFontMetric(hMetric); 1343 return nullptr; 1344 } 1345 1346 vcl::Font FindFont_FallbackToDefault(std::u16string_view rFontName) 1347 { 1348 if (auto pFound = FindFont(rFontName)) 1349 return *pFound; 1350 1351 return OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, LANGUAGE_NONE, 1352 GetDefaultFontFlags::NONE); 1353 } 1354 1355 int getDocumentType (LibreOfficeKitDocument* pThis) 1356 { 1357 SetLastExceptionMsg(); 1358 1359 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 1360 1361 try 1362 { 1363 uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW); 1364 1365 if (xDocument->supportsService(u"com.sun.star.sheet.SpreadsheetDocument"_ustr)) 1366 { 1367 return LOK_DOCTYPE_SPREADSHEET; 1368 } 1369 else if (xDocument->supportsService(u"com.sun.star.presentation.PresentationDocument"_ustr)) 1370 { 1371 return LOK_DOCTYPE_PRESENTATION; 1372 } 1373 else if (xDocument->supportsService(u"com.sun.star.drawing.DrawingDocument"_ustr)) 1374 { 1375 return LOK_DOCTYPE_DRAWING; 1376 } 1377 else if (xDocument->supportsService(u"com.sun.star.text.TextDocument"_ustr) || xDocument->supportsService(u"com.sun.star.text.WebDocument"_ustr)) 1378 { 1379 return LOK_DOCTYPE_TEXT; 1380 } 1381 else 1382 { 1383 SetLastExceptionMsg(u"unknown document type"_ustr); 1384 } 1385 } 1386 catch (const uno::Exception& exception) 1387 { 1388 SetLastExceptionMsg("exception: " + exception.Message); 1389 } 1390 return LOK_DOCTYPE_OTHER; 1391 } 1392 1393 } // anonymous namespace 1394 1395 LibLODocument_Impl::LibLODocument_Impl(uno::Reference <css::lang::XComponent> xComponent, int nDocumentId) 1396 : mxComponent(std::move(xComponent)) 1397 , mnDocumentId(nDocumentId) 1398 { 1399 assert(nDocumentId != -1 && "Cannot set mnDocumentId to -1"); 1400 1401 m_pDocumentClass = gDocumentClass.lock(); 1402 if (!m_pDocumentClass) 1403 { 1404 m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>(); 1405 1406 m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass); 1407 1408 m_pDocumentClass->destroy = doc_destroy; 1409 m_pDocumentClass->saveAs = doc_saveAs; 1410 m_pDocumentClass->getDocumentType = doc_getDocumentType; 1411 m_pDocumentClass->getParts = doc_getParts; 1412 m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles; 1413 m_pDocumentClass->getPart = doc_getPart; 1414 m_pDocumentClass->setPart = doc_setPart; 1415 m_pDocumentClass->selectPart = doc_selectPart; 1416 m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts; 1417 m_pDocumentClass->getPartName = doc_getPartName; 1418 m_pDocumentClass->setPartMode = doc_setPartMode; 1419 m_pDocumentClass->getEditMode = doc_getEditMode; 1420 m_pDocumentClass->paintTile = doc_paintTile; 1421 m_pDocumentClass->paintPartTile = doc_paintPartTile; 1422 m_pDocumentClass->getTileMode = doc_getTileMode; 1423 m_pDocumentClass->getDocumentSize = doc_getDocumentSize; 1424 m_pDocumentClass->getDataArea = doc_getDataArea; 1425 m_pDocumentClass->initializeForRendering = doc_initializeForRendering; 1426 m_pDocumentClass->registerCallback = doc_registerCallback; 1427 m_pDocumentClass->postKeyEvent = doc_postKeyEvent; 1428 m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent; 1429 m_pDocumentClass->hyperlinkInfoAtPosition = doc_hyperlinkInfoAtPosition; 1430 m_pDocumentClass->removeTextContext = doc_removeTextContext; 1431 m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent; 1432 m_pDocumentClass->postMouseEvent = doc_postMouseEvent; 1433 m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent; 1434 m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent; 1435 m_pDocumentClass->postUnoCommand = doc_postUnoCommand; 1436 m_pDocumentClass->setTextSelection = doc_setTextSelection; 1437 m_pDocumentClass->setWindowTextSelection = doc_setWindowTextSelection; 1438 m_pDocumentClass->getTextSelection = doc_getTextSelection; 1439 m_pDocumentClass->getSelectionType = doc_getSelectionType; 1440 m_pDocumentClass->getSelectionTypeAndText = doc_getSelectionTypeAndText; 1441 m_pDocumentClass->getClipboard = doc_getClipboard; 1442 m_pDocumentClass->setClipboard = doc_setClipboard; 1443 m_pDocumentClass->paste = doc_paste; 1444 m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection; 1445 m_pDocumentClass->resetSelection = doc_resetSelection; 1446 m_pDocumentClass->getCommandValues = doc_getCommandValues; 1447 m_pDocumentClass->setClientZoom = doc_setClientZoom; 1448 m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea; 1449 m_pDocumentClass->setOutlineState = doc_setOutlineState; 1450 1451 m_pDocumentClass->createView = doc_createView; 1452 m_pDocumentClass->destroyView = doc_destroyView; 1453 m_pDocumentClass->setView = doc_setView; 1454 m_pDocumentClass->getView = doc_getView; 1455 m_pDocumentClass->getViewsCount = doc_getViewsCount; 1456 m_pDocumentClass->getViewIds = doc_getViewIds; 1457 1458 m_pDocumentClass->renderFont = doc_renderFont; 1459 m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation; 1460 m_pDocumentClass->getPartHash = doc_getPartHash; 1461 1462 m_pDocumentClass->paintWindow = doc_paintWindow; 1463 m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI; 1464 m_pDocumentClass->paintWindowForView = doc_paintWindowForView; 1465 m_pDocumentClass->postWindow = doc_postWindow; 1466 m_pDocumentClass->resizeWindow = doc_resizeWindow; 1467 1468 m_pDocumentClass->setViewLanguage = doc_setViewLanguage; 1469 1470 m_pDocumentClass->getPartInfo = doc_getPartInfo; 1471 1472 m_pDocumentClass->insertCertificate = doc_insertCertificate; 1473 m_pDocumentClass->addCertificate = doc_addCertificate; 1474 m_pDocumentClass->getSignatureState = doc_getSignatureState; 1475 1476 m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection; 1477 m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent; 1478 1479 m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions; 1480 m_pDocumentClass->completeFunction = doc_completeFunction; 1481 1482 m_pDocumentClass->sendFormFieldEvent = doc_sendFormFieldEvent; 1483 m_pDocumentClass->renderSearchResult = doc_renderSearchResult; 1484 1485 m_pDocumentClass->setBlockedCommandList = doc_setBlockedCommandList; 1486 1487 m_pDocumentClass->sendContentControlEvent = doc_sendContentControlEvent; 1488 1489 m_pDocumentClass->setViewTimezone = doc_setViewTimezone; 1490 1491 m_pDocumentClass->setAccessibilityState = doc_setAccessibilityState; 1492 1493 m_pDocumentClass->getA11yFocusedParagraph = doc_getA11yFocusedParagraph; 1494 m_pDocumentClass->getA11yCaretPosition = doc_getA11yCaretPosition; 1495 1496 gDocumentClass = m_pDocumentClass; 1497 } 1498 pClass = m_pDocumentClass.get(); 1499 1500 #ifndef IOS 1501 forceSetClipboardForCurrentView(this); 1502 #endif 1503 } 1504 1505 LibLODocument_Impl::~LibLODocument_Impl() 1506 { 1507 try 1508 { 1509 mxComponent->dispose(); 1510 } 1511 catch (const css::lang::DisposedException&) 1512 { 1513 TOOLS_WARN_EXCEPTION("lok", "failed to dispose document"); 1514 } 1515 } 1516 1517 static OUString getGenerator() 1518 { 1519 OUString sGenerator( 1520 Translate::ExpandVariables(u"%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"_ustr)); 1521 OUString os(u"$_OS"_ustr); 1522 ::rtl::Bootstrap::expandMacros(os); 1523 return sGenerator.replaceFirst("%1", os); 1524 } 1525 1526 extern "C" { 1527 1528 CallbackFlushHandler::TimeoutIdle::TimeoutIdle( CallbackFlushHandler* handler ) 1529 : Timer( "lokit timer callback" ) 1530 , mHandler( handler ) 1531 { 1532 // A second timer with higher priority, it'll ensure we flush in reasonable time if we get too busy 1533 // to get POST_PAINT priority processing. Otherwise it could take a long time to flush. 1534 SetPriority(TaskPriority::DEFAULT); 1535 SetTimeout( 100 ); // 100 ms 1536 } 1537 1538 void CallbackFlushHandler::TimeoutIdle::Invoke() 1539 { 1540 mHandler->Invoke(); 1541 } 1542 1543 // One of these is created per view to handle events cf. doc_registerCallback 1544 CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData) 1545 : Idle( "lokit idle callback" ), 1546 m_pDocument(pDocument), 1547 m_pCallback(pCallback), 1548 m_pData(pData), 1549 m_nDisableCallbacks(0), 1550 m_TimeoutIdle( this ) 1551 { 1552 SetPriority(TaskPriority::POST_PAINT); 1553 1554 // Add the states that are safe to skip duplicates on, even when 1555 // not consequent (i.e. do no emit them if unchanged from last). 1556 m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL"_ostr); 1557 m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL"_ostr); 1558 m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL"_ostr); 1559 m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL"_ostr); 1560 m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL"_ostr); 1561 m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL"_ostr); 1562 m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL"_ostr); 1563 m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL"_ostr); 1564 m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL"_ostr); 1565 m_states.emplace(LOK_CALLBACK_SET_PART, "NIL"_ostr); 1566 m_states.emplace(LOK_CALLBACK_TABLE_SELECTED, "NIL"_ostr); 1567 m_states.emplace(LOK_CALLBACK_TAB_STOP_LIST, "NIL"_ostr); 1568 m_states.emplace(LOK_CALLBACK_RULER_UPDATE, "NIL"_ostr); 1569 m_states.emplace(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE, "NIL"_ostr); 1570 } 1571 1572 CallbackFlushHandler::~CallbackFlushHandler() 1573 { 1574 Stop(); 1575 } 1576 1577 CallbackFlushHandler::queue_type2::iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::iterator pos) 1578 { 1579 int delta = std::distance(m_queue1.begin(), pos); 1580 return m_queue2.begin() + delta; 1581 } 1582 1583 CallbackFlushHandler::queue_type2::reverse_iterator CallbackFlushHandler::toQueue2(CallbackFlushHandler::queue_type1::reverse_iterator pos) 1584 { 1585 int delta = std::distance(m_queue1.rbegin(), pos); 1586 return m_queue2.rbegin() + delta; 1587 } 1588 1589 void CallbackFlushHandler::setUpdatedType( int nType, bool value ) 1590 { 1591 assert(isUpdatedType(nType)); 1592 if( m_updatedTypes.size() <= o3tl::make_unsigned( nType )) 1593 m_updatedTypes.resize( nType + 1 ); // new are default-constructed, i.e. false 1594 m_updatedTypes[ nType ] = value; 1595 if(value) 1596 startTimer(); 1597 } 1598 1599 void CallbackFlushHandler::resetUpdatedType( int nType ) 1600 { 1601 setUpdatedType( nType, false ); 1602 } 1603 1604 void CallbackFlushHandler::setUpdatedTypePerViewId( int nType, int nViewId, int nSourceViewId, bool value ) 1605 { 1606 assert(isUpdatedTypePerViewId(nType)); 1607 std::vector<PerViewIdData>& types = m_updatedTypesPerViewId[ nViewId ]; 1608 if( types.size() <= o3tl::make_unsigned( nType )) 1609 types.resize( nType + 1 ); // new are default-constructed, i.e. 'set' is false 1610 types[ nType ] = PerViewIdData{ value, nSourceViewId }; 1611 if(value) 1612 startTimer(); 1613 } 1614 1615 void CallbackFlushHandler::resetUpdatedTypePerViewId( int nType, int nViewId ) 1616 { 1617 assert(isUpdatedTypePerViewId(nType)); 1618 bool allViewIds = false; 1619 // Handle specially messages that do not have viewId for backwards compatibility. 1620 if( nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && !comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) 1621 allViewIds = true; 1622 if( !allViewIds ) 1623 { 1624 setUpdatedTypePerViewId( nType, nViewId, -1, false ); 1625 return; 1626 } 1627 for( auto& it : m_updatedTypesPerViewId ) 1628 { 1629 std::vector<PerViewIdData>& types = it.second; 1630 if( types.size() >= o3tl::make_unsigned( nType )) 1631 types[ nType ].set = false; 1632 } 1633 } 1634 1635 void CallbackFlushHandler::libreOfficeKitViewCallback(int nType, const OString& pPayload) 1636 { 1637 CallbackData callbackData(pPayload); 1638 queue(nType, callbackData); 1639 } 1640 1641 void CallbackFlushHandler::libreOfficeKitViewCallbackWithViewId(int nType, const OString& pPayload, int nViewId) 1642 { 1643 CallbackData callbackData(pPayload, nViewId); 1644 queue(nType, callbackData); 1645 } 1646 1647 void CallbackFlushHandler::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart, int nMode) 1648 { 1649 CallbackData callbackData(pRect, nPart, nMode); 1650 queue(LOK_CALLBACK_INVALIDATE_TILES, callbackData); 1651 } 1652 1653 void CallbackFlushHandler::libreOfficeKitViewUpdatedCallback(int nType) 1654 { 1655 assert(isUpdatedType( nType )); 1656 std::unique_lock<std::recursive_mutex> lock(m_mutex); 1657 SAL_INFO("lok", "Updated: [" << nType << "]"); 1658 setUpdatedType(nType, true); 1659 } 1660 1661 void CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType, int nViewId, int nSourceViewId) 1662 { 1663 assert(isUpdatedTypePerViewId( nType )); 1664 std::unique_lock<std::recursive_mutex> lock(m_mutex); 1665 SAL_INFO("lok", "Updated: [" << nType << "]"); 1666 setUpdatedTypePerViewId(nType, nViewId, nSourceViewId, true); 1667 } 1668 1669 void CallbackFlushHandler::dumpState(rtl::OStringBuffer &rState) 1670 { 1671 // NB. no locking 1672 rState.append("\nView:\t"); 1673 rState.append(static_cast<sal_Int32>(m_viewId)); 1674 rState.append("\n\tDisableCallbacks:\t"); 1675 rState.append(static_cast<sal_Int32>(m_nDisableCallbacks)); 1676 rState.append("\n\tStates:\n"); 1677 for (const auto &i : m_states) 1678 { 1679 rState.append("\n\t\t"); 1680 rState.append(static_cast<sal_Int32>(i.first)); 1681 rState.append("\t"); 1682 rState.append(i.second); 1683 } 1684 } 1685 1686 void CallbackFlushHandler::libreOfficeKitViewAddPendingInvalidateTiles() 1687 { 1688 // Invoke() will call flushPendingLOKInvalidateTiles(), so just make sure the timer is active. 1689 startTimer(); 1690 } 1691 1692 void CallbackFlushHandler::queue(const int type, const OString& data) 1693 { 1694 CallbackData callbackData(data); 1695 queue(type, callbackData); 1696 } 1697 1698 void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData) 1699 { 1700 comphelper::ProfileZone aZone("CallbackFlushHandler::queue"); 1701 1702 SAL_INFO("lok", "Queue: [" << type << "]: [" << aCallbackData.getPayload() << "] on " << m_queue1.size() << " entries."); 1703 1704 bool bIsChartActive = false; 1705 bool bIsComment = false; 1706 if (type == LOK_CALLBACK_GRAPHIC_SELECTION) 1707 { 1708 LokChartHelper aChartHelper(SfxViewShell::Current()); 1709 bIsChartActive = aChartHelper.GetWindow() != nullptr; 1710 } 1711 else if (type == LOK_CALLBACK_COMMENT) 1712 { 1713 bIsComment = true; 1714 } 1715 1716 if (callbacksDisabled() && !bIsChartActive && !bIsComment) 1717 { 1718 // We drop notifications when this is set, except for important ones. 1719 // When we issue a complex command (such as .uno:InsertAnnotation) 1720 // there will be multiple notifications. On the first invalidation 1721 // we will start painting, but other events will get fired 1722 // while the complex command in question executes. 1723 // We don't want to suppress everything here on the wrong assumption 1724 // that no new events are fired during painting. 1725 if (type != LOK_CALLBACK_STATE_CHANGED && 1726 type != LOK_CALLBACK_INVALIDATE_TILES && 1727 type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && 1728 type != LOK_CALLBACK_CURSOR_VISIBLE && 1729 type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE && 1730 type != LOK_CALLBACK_TEXT_SELECTION && 1731 type != LOK_CALLBACK_TEXT_SELECTION_START && 1732 type != LOK_CALLBACK_TEXT_SELECTION_END && 1733 type != LOK_CALLBACK_MEDIA_SHAPE && 1734 type != LOK_CALLBACK_REFERENCE_MARKS) 1735 { 1736 SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << aCallbackData.getPayload() << "]."); 1737 return; 1738 } 1739 1740 // In Writer we drop all notifications during painting. 1741 if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT) 1742 return; 1743 } 1744 1745 // Suppress invalid payloads. 1746 if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR && 1747 aCallbackData.getPayload().indexOf(", 0, 0, ") != -1 && 1748 aCallbackData.getPayload().indexOf("\"hyperlink\":\"\"") == -1 && 1749 aCallbackData.getPayload().indexOf("\"hyperlink\": {}") == -1) 1750 { 1751 // The cursor position is often the relative coordinates of the widget 1752 // issuing it, instead of the absolute one that we expect. 1753 // This is temporary however, and, once the control is created and initialized 1754 // correctly, it eventually emits the correct absolute coordinates. 1755 SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << aCallbackData.getPayload() << "]."); 1756 return; 1757 } 1758 1759 std::unique_lock<std::recursive_mutex> lock(m_mutex); 1760 1761 // Update types should be received via the updated callbacks for performance, 1762 // getting them as normal callbacks is technically not wrong, but probably should be avoided. 1763 // Reset the updated flag if we get a normal message. 1764 if(isUpdatedType(type)) 1765 { 1766 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback"); 1767 resetUpdatedType(type); 1768 } 1769 if(isUpdatedTypePerViewId(type)) 1770 { 1771 SAL_INFO("lok", "Received event with updated type [" << type << "] as normal callback"); 1772 resetUpdatedTypePerViewId(type, aCallbackData.getViewId()); 1773 } 1774 1775 // drop duplicate callbacks for the listed types 1776 switch (type) 1777 { 1778 case LOK_CALLBACK_TEXT_SELECTION_START: 1779 case LOK_CALLBACK_TEXT_SELECTION_END: 1780 case LOK_CALLBACK_TEXT_SELECTION: 1781 case LOK_CALLBACK_GRAPHIC_SELECTION: 1782 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: 1783 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: 1784 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: 1785 case LOK_CALLBACK_STATE_CHANGED: 1786 case LOK_CALLBACK_MOUSE_POINTER: 1787 case LOK_CALLBACK_CELL_CURSOR: 1788 case LOK_CALLBACK_CELL_VIEW_CURSOR: 1789 case LOK_CALLBACK_CELL_FORMULA: 1790 case LOK_CALLBACK_CELL_ADDRESS: 1791 case LOK_CALLBACK_CELL_SELECTION_AREA: 1792 case LOK_CALLBACK_CURSOR_VISIBLE: 1793 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: 1794 case LOK_CALLBACK_SET_PART: 1795 case LOK_CALLBACK_TEXT_VIEW_SELECTION: 1796 case LOK_CALLBACK_INVALIDATE_HEADER: 1797 case LOK_CALLBACK_WINDOW: 1798 case LOK_CALLBACK_CALC_FUNCTION_LIST: 1799 case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY: 1800 case LOK_CALLBACK_REFERENCE_MARKS: 1801 case LOK_CALLBACK_CELL_AUTO_FILL_AREA: 1802 case LOK_CALLBACK_A11Y_FOCUS_CHANGED: 1803 case LOK_CALLBACK_A11Y_CARET_CHANGED: 1804 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: 1805 case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED: 1806 case LOK_CALLBACK_COLOR_PALETTES: 1807 case LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE: 1808 case LOK_CALLBACK_A11Y_SELECTION_CHANGED: 1809 { 1810 const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), type); 1811 auto pos2 = toQueue2(pos); 1812 if (pos != m_queue1.rend() && pos2->getPayload() == aCallbackData.getPayload()) 1813 { 1814 SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << aCallbackData.getPayload() << "]."); 1815 return; 1816 } 1817 } 1818 break; 1819 } 1820 1821 if (type == LOK_CALLBACK_TEXT_SELECTION && aCallbackData.isEmpty()) 1822 { 1823 const auto& posStart = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_START); 1824 auto posStart2 = toQueue2(posStart); 1825 if (posStart != m_queue1.rend()) 1826 posStart2->clear(); 1827 1828 const auto& posEnd = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_TEXT_SELECTION_END); 1829 auto posEnd2 = toQueue2(posEnd); 1830 if (posEnd != m_queue1.rend()) 1831 posEnd2->clear(); 1832 } 1833 1834 // When payload is empty discards any previous state. 1835 if (aCallbackData.isEmpty()) 1836 { 1837 switch (type) 1838 { 1839 case LOK_CALLBACK_TEXT_SELECTION_START: 1840 case LOK_CALLBACK_TEXT_SELECTION_END: 1841 case LOK_CALLBACK_TEXT_SELECTION: 1842 case LOK_CALLBACK_GRAPHIC_SELECTION: 1843 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: 1844 case LOK_CALLBACK_INVALIDATE_TILES: 1845 case LOK_CALLBACK_TOOLTIP: 1846 if (removeAll(type)) 1847 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "]."); 1848 break; 1849 } 1850 } 1851 else 1852 { 1853 switch (type) 1854 { 1855 // These are safe to use the latest state and ignore previous 1856 // ones (if any) since the last overrides previous ones. 1857 case LOK_CALLBACK_TEXT_SELECTION_START: 1858 case LOK_CALLBACK_TEXT_SELECTION_END: 1859 case LOK_CALLBACK_TEXT_SELECTION: 1860 case LOK_CALLBACK_MOUSE_POINTER: 1861 case LOK_CALLBACK_CELL_CURSOR: 1862 case LOK_CALLBACK_CELL_FORMULA: 1863 case LOK_CALLBACK_CELL_ADDRESS: 1864 case LOK_CALLBACK_CURSOR_VISIBLE: 1865 case LOK_CALLBACK_SET_PART: 1866 case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE: 1867 case LOK_CALLBACK_RULER_UPDATE: 1868 case LOK_CALLBACK_A11Y_FOCUS_CHANGED: 1869 case LOK_CALLBACK_A11Y_CARET_CHANGED: 1870 case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED: 1871 case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED: 1872 case LOK_CALLBACK_COLOR_PALETTES: 1873 case LOK_CALLBACK_TOOLTIP: 1874 { 1875 if (removeAll(type)) 1876 SAL_INFO("lok", "Removed dups of [" << type << "]: [" << aCallbackData.getPayload() << "]."); 1877 } 1878 break; 1879 1880 // These are safe to use the latest state and ignore previous 1881 // ones (if any) since the last overrides previous ones, 1882 // but only if the view is the same. 1883 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: 1884 // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place. 1885 // If the hyperlink is not empty we can bypass that to show the popup 1886 if (aCallbackData.getPayload().indexOf("\"hyperlink\":\"\"") == -1 1887 && aCallbackData.getPayload().indexOf("\"hyperlink\": {}") == -1) 1888 break; 1889 [[fallthrough]]; 1890 case LOK_CALLBACK_CELL_VIEW_CURSOR: 1891 case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: 1892 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: 1893 case LOK_CALLBACK_TEXT_VIEW_SELECTION: 1894 case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: 1895 case LOK_CALLBACK_CALC_FUNCTION_LIST: 1896 case LOK_CALLBACK_FORM_FIELD_BUTTON: 1897 { 1898 const int nViewId = aCallbackData.getViewId(); 1899 removeAll(type, [nViewId] (const CallbackData& elemData) { 1900 return (nViewId == elemData.getViewId()); 1901 } 1902 ); 1903 } 1904 break; 1905 1906 case LOK_CALLBACK_INVALIDATE_TILES: 1907 if (processInvalidateTilesEvent(type, aCallbackData)) 1908 return; 1909 break; 1910 1911 // State changes with same name override previous ones with a different value. 1912 // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus. 1913 case LOK_CALLBACK_STATE_CHANGED: 1914 { 1915 // Compare the state name=value and overwrite earlier entries with same name. 1916 const auto pos = aCallbackData.getPayload().indexOf('='); 1917 if (pos != -1) 1918 { 1919 const std::string_view name = aCallbackData.getPayload().subView(0, pos + 1); 1920 // This is needed because otherwise it creates some problems when 1921 // a save occurs while a cell is still edited in Calc. 1922 if (name != ".uno:ModifiedStatus=") 1923 { 1924 removeAll(type, [&name] (const CallbackData& elemData) { 1925 return elemData.getPayload().startsWith(name); 1926 } 1927 ); 1928 } 1929 } 1930 } 1931 break; 1932 1933 case LOK_CALLBACK_WINDOW: 1934 if (processWindowEvent(type, aCallbackData)) 1935 return; 1936 break; 1937 1938 case LOK_CALLBACK_GRAPHIC_SELECTION: 1939 { 1940 // remove only selection ranges and 'EMPTY' messages 1941 // always send 'INPLACE' and 'INPLACE EXIT' messages 1942 removeAll(type, [] (const CallbackData& elemData) 1943 { return (elemData.getPayload().indexOf("INPLACE") == -1); }); 1944 } 1945 break; 1946 } 1947 } 1948 1949 // Validate that the cached data and the payload string are identical. 1950 assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!"); 1951 m_queue1.emplace_back(type); 1952 m_queue2.emplace_back(aCallbackData); 1953 SAL_INFO("lok", "Queued #" << (m_queue1.size() - 1) << 1954 " [" << type << "]: [" << aCallbackData.getPayload() << "] to have " << m_queue1.size() << " entries."); 1955 1956 #ifdef DBG_UTIL 1957 { 1958 // Dump the queue state and validate cached data. 1959 int i = 1; 1960 std::ostringstream oss; 1961 if (m_queue1.empty()) 1962 oss << "Empty"; 1963 else 1964 oss << m_queue1.size() << " items\n"; 1965 auto it1 = m_queue1.begin(); 1966 auto it2 = m_queue2.begin(); 1967 for (; it1 != m_queue1.end(); ++it1, ++it2) 1968 oss << i++ << ": [" << *it1 << "] [" << it2->getPayload() << "].\n"; 1969 SAL_INFO("lok", "Current Queue: " << oss.str()); 1970 assert( 1971 std::all_of( 1972 m_queue2.begin(), m_queue2.end(), 1973 [](const CallbackData& c) { return c.validate(); })); 1974 } 1975 #endif 1976 1977 lock.unlock(); 1978 startTimer(); 1979 } 1980 1981 bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& aCallbackData) 1982 { 1983 RectangleAndPart rcNew = aCallbackData.getRectangleAndPart(); 1984 if (rcNew.isEmpty()) 1985 { 1986 SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << aCallbackData.getPayload() << "]."); 1987 return true; 1988 } 1989 1990 // If we have to invalidate all tiles, we can skip any new tile invalidation. 1991 // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all. 1992 const auto& pos 1993 = std::find(m_queue1.rbegin(), m_queue1.rend(), LOK_CALLBACK_INVALIDATE_TILES); 1994 if (pos != m_queue1.rend()) 1995 { 1996 auto pos2 = toQueue2(pos); 1997 const RectangleAndPart& rcOld = pos2->getRectangleAndPart(); 1998 if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) && 1999 (rcOld.m_nMode == rcNew.m_nMode)) 2000 { 2001 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload() 2002 << "] since all tiles need to be invalidated."); 2003 return true; 2004 } 2005 2006 if ((rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) && (rcOld.m_nMode == rcNew.m_nMode)) 2007 { 2008 // If fully overlapping. 2009 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle)) 2010 { 2011 SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload() 2012 << "] since overlaps existing all-parts."); 2013 return true; 2014 } 2015 } 2016 } 2017 2018 if (rcNew.isInfinite()) 2019 { 2020 SAL_INFO("lok", "Have Empty [" << type << "]: [" << aCallbackData.getPayload() 2021 << "] so removing all with part " << rcNew.m_nPart << "."); 2022 removeAll(LOK_CALLBACK_INVALIDATE_TILES, [&rcNew](const CallbackData& elemData) { 2023 // Remove exiting if new is all-encompassing, or if of the same part. 2024 return ((rcNew.m_nPart == -1 || rcNew.m_nPart == elemData.getRectangleAndPart().m_nPart) 2025 && (rcNew.m_nMode == elemData.getRectangleAndPart().m_nMode)); 2026 }); 2027 } 2028 else 2029 { 2030 const auto rcOrig = rcNew; 2031 2032 SAL_INFO("lok", "Have [" << type << "]: [" << aCallbackData.getPayload() << "] so merging overlapping."); 2033 removeAll(LOK_CALLBACK_INVALIDATE_TILES,[&rcNew](const CallbackData& elemData) { 2034 const RectangleAndPart& rcOld = elemData.getRectangleAndPart(); 2035 if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && 2036 (rcOld.m_nPart != rcNew.m_nPart || rcOld.m_nMode != rcNew.m_nMode)) 2037 { 2038 SAL_INFO("lok", "Nothing to merge between new: " 2039 << rcNew.toString() << ", and old: " << rcOld.toString()); 2040 return false; 2041 } 2042 2043 if (rcNew.m_nPart == -1) 2044 { 2045 // Don't merge unless fully overlapped. 2046 SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString() 2047 << "?"); 2048 if (rcNew.m_aRectangle.Contains(rcOld.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode) 2049 { 2050 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old " 2051 << rcOld.toString() << "."); 2052 return true; 2053 } 2054 } 2055 else if (rcOld.m_nPart == -1) 2056 { 2057 // Don't merge unless fully overlapped. 2058 SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString() 2059 << "?"); 2060 if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode) 2061 { 2062 SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old " 2063 << rcOld.toString() << "."); 2064 return true; 2065 } 2066 } 2067 else 2068 { 2069 const tools::Rectangle rcOverlap 2070 = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle); 2071 const bool bOverlap = !rcOverlap.IsEmpty() && rcOld.m_nMode == rcNew.m_nMode; 2072 SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString() 2073 << " => " << rcOverlap.toString() 2074 << " Overlap: " << bOverlap); 2075 if (bOverlap) 2076 { 2077 rcNew.m_aRectangle.Union(rcOld.m_aRectangle); 2078 SAL_INFO("lok", "Merged: " << rcNew.toString()); 2079 return true; 2080 } 2081 } 2082 2083 // Keep others. 2084 return false; 2085 }); 2086 2087 if (rcNew.m_aRectangle != rcOrig.m_aRectangle) 2088 { 2089 SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString()); 2090 if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth() 2091 || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight()) 2092 { 2093 SAL_WARN("lok", "Error: merged rect smaller."); 2094 } 2095 } 2096 } 2097 2098 aCallbackData.updateRectangleAndPart(rcNew); 2099 // Queue this one. 2100 return false; 2101 } 2102 2103 bool CallbackFlushHandler::processWindowEvent(int type, CallbackData& aCallbackData) 2104 { 2105 const OString& payload = aCallbackData.getPayload(); 2106 2107 boost::property_tree::ptree& aTree = aCallbackData.setJson(std::string(payload)); 2108 const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0); 2109 const std::string aAction = aTree.get<std::string>("action", ""); 2110 if (aAction == "invalidate") 2111 { 2112 std::string aRectStr = aTree.get<std::string>("rectangle", ""); 2113 // no 'rectangle' field => invalidate all of the window => 2114 // remove all previous window part invalidations 2115 if (aRectStr.empty()) 2116 { 2117 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) { 2118 const boost::property_tree::ptree& aOldTree = elemData.getJson(); 2119 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0) 2120 && aOldTree.get<std::string>("action", "") == "invalidate") 2121 { 2122 return true; 2123 } 2124 return false; 2125 }); 2126 } 2127 else 2128 { 2129 // if we have to invalidate all of the window, ignore 2130 // any part invalidation message 2131 bool invAllExist = false; 2132 auto it1 = m_queue1.rbegin(); 2133 auto it2 = m_queue2.rbegin(); 2134 for (;it1 != m_queue1.rend(); ++it1, ++it2) 2135 { 2136 if (*it1 != LOK_CALLBACK_WINDOW) 2137 continue; 2138 const boost::property_tree::ptree& aOldTree = it2->getJson(); 2139 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0) 2140 && aOldTree.get<std::string>("action", "") == "invalidate" 2141 && aOldTree.get<std::string>("rectangle", "").empty()) 2142 { 2143 invAllExist = true; 2144 break; 2145 } 2146 } 2147 2148 // we found a invalidate-all window callback 2149 if (invAllExist) 2150 { 2151 SAL_INFO("lok.dialog", "Skipping queue [" 2152 << type << "]: [" << payload 2153 << "] since whole window needs to be invalidated."); 2154 return true; 2155 } 2156 2157 std::istringstream aRectStream(aRectStr); 2158 tools::Long nLeft, nTop, nWidth, nHeight; 2159 char nComma; 2160 aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight; 2161 tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight); 2162 bool currentIsRedundant = false; 2163 removeAll(LOK_CALLBACK_WINDOW, [&aNewRect, &nLOKWindowId, 2164 ¤tIsRedundant](const CallbackData& elemData) { 2165 const boost::property_tree::ptree& aOldTree = elemData.getJson(); 2166 if (aOldTree.get<std::string>("action", "") == "invalidate") 2167 { 2168 // Not possible that we encounter an empty rectangle here; we already handled this case above. 2169 std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", "")); 2170 tools::Long nOldLeft, nOldTop, nOldWidth, nOldHeight; 2171 char nOldComma; 2172 aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth 2173 >> nOldComma >> nOldHeight; 2174 const tools::Rectangle aOldRect = tools::Rectangle( 2175 nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight); 2176 2177 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)) 2178 { 2179 if (aNewRect == aOldRect) 2180 { 2181 SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString() 2182 << "]. Skipping new."); 2183 // We have a rectangle in the queue already that makes the current Callback useless. 2184 currentIsRedundant = true; 2185 return false; 2186 } 2187 // new one engulfs the old one? 2188 else if (aNewRect.Contains(aOldRect)) 2189 { 2190 SAL_INFO("lok.dialog", 2191 "New rect [" << aNewRect.toString() << "] engulfs old [" 2192 << aOldRect.toString() << "]. Replacing old."); 2193 return true; 2194 } 2195 // old one engulfs the new one? 2196 else if (aOldRect.Contains(aNewRect)) 2197 { 2198 SAL_INFO("lok.dialog", 2199 "Old rect [" << aOldRect.toString() << "] engulfs new [" 2200 << aNewRect.toString() << "]. Skipping new."); 2201 // We have a rectangle in the queue already that makes the current Callback useless. 2202 currentIsRedundant = true; 2203 return false; 2204 } 2205 else 2206 { 2207 // Overlapping rects. 2208 const tools::Rectangle aPreMergeRect = aNewRect; 2209 aNewRect.Union(aOldRect); 2210 SAL_INFO("lok.dialog", "Merging rects [" 2211 << aPreMergeRect.toString() << "] & [" 2212 << aOldRect.toString() << "] = [" 2213 << aNewRect.toString() 2214 << "]. Replacing old."); 2215 return true; 2216 } 2217 } 2218 } 2219 2220 // keep rest 2221 return false; 2222 }); 2223 2224 // Do not enqueue if redundant. 2225 if (currentIsRedundant) 2226 return true; 2227 2228 aTree.put("rectangle", aNewRect.toString().getStr()); 2229 aCallbackData.setJson(aTree); 2230 assert(aCallbackData.validate() && "Validation after setJson failed!"); 2231 } 2232 } 2233 else if (aAction == "created") 2234 { 2235 // Remove all previous actions on same dialog, if we are creating it anew. 2236 removeAll(LOK_CALLBACK_WINDOW,[&nLOKWindowId](const CallbackData& elemData) { 2237 const boost::property_tree::ptree& aOldTree = elemData.getJson(); 2238 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)) 2239 return true; 2240 return false; 2241 }); 2242 2243 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 2244 if (!pWindow) 2245 { 2246 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 2247 return false; 2248 } 2249 2250 #ifndef IOS 2251 auto xClip = forceSetClipboardForCurrentView(m_pDocument); 2252 2253 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip); 2254 pWindow->SetClipboard(xClipboard); 2255 #endif 2256 } 2257 else if (aAction == "size_changed") 2258 { 2259 // A size change is practically re-creation of the window. 2260 // But at a minimum it's a full invalidation. 2261 removeAll(LOK_CALLBACK_WINDOW, [&nLOKWindowId](const CallbackData& elemData) { 2262 const boost::property_tree::ptree& aOldTree = elemData.getJson(); 2263 if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)) 2264 { 2265 const std::string aOldAction = aOldTree.get<std::string>("action", ""); 2266 if (aOldAction == "invalidate") 2267 return true; 2268 } 2269 return false; 2270 }); 2271 } 2272 2273 // Queue this one. 2274 return false; 2275 } 2276 2277 void CallbackFlushHandler::enqueueUpdatedTypes() 2278 { 2279 if( m_updatedTypes.empty() && m_updatedTypesPerViewId.empty()) 2280 return; 2281 assert(m_viewId >= 0); 2282 SfxViewShell* viewShell = SfxViewShell::GetFirst( false, 2283 [this](const SfxViewShell& shell) { return shell.GetViewShellId().get() == m_viewId; } ); 2284 assert(viewShell != nullptr); 2285 2286 // First move data to local structures, so that callbacks don't possibly modify it. 2287 std::vector<bool> updatedTypes; 2288 std::swap(updatedTypes, m_updatedTypes); 2289 boost::container::flat_map<int, std::vector<PerViewIdData>> updatedTypesPerViewId; 2290 std::swap(updatedTypesPerViewId, m_updatedTypesPerViewId); 2291 2292 // Some types must always precede other types, for example 2293 // LOK_CALLBACK_TEXT_SELECTION_START and LOK_CALLBACK_TEXT_SELECTION_END 2294 // must always precede LOK_CALLBACK_TEXT_SELECTION if present. 2295 // Only these types should be present (see isUpdatedType()) and should be processed in this order. 2296 static const int orderedUpdatedTypes[] = { 2297 LOK_CALLBACK_TEXT_SELECTION_START, LOK_CALLBACK_TEXT_SELECTION_END, LOK_CALLBACK_TEXT_SELECTION }; 2298 // Only these types should be present (see isUpdatedTypePerViewId()) and (as of now) 2299 // the order doesn't matter. 2300 static const int orderedUpdatedTypesPerViewId[] = { 2301 LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, 2302 LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, 2303 LOK_CALLBACK_TEXT_VIEW_SELECTION }; 2304 2305 for( int type : orderedUpdatedTypes ) 2306 { 2307 if(o3tl::make_unsigned( type ) < updatedTypes.size() && updatedTypes[ type ]) 2308 { 2309 enqueueUpdatedType( type, viewShell, m_viewId ); 2310 } 2311 } 2312 for( const auto& it : updatedTypesPerViewId ) 2313 { 2314 int viewId = it.first; 2315 const std::vector<PerViewIdData>& types = it.second; 2316 for( int type : orderedUpdatedTypesPerViewId ) 2317 { 2318 if(o3tl::make_unsigned( type ) < types.size() && types[ type ].set) 2319 { 2320 SfxViewShell* sourceViewShell = viewShell; 2321 const int sourceViewId = types[ type ].sourceViewId; 2322 if( sourceViewId != m_viewId ) 2323 { 2324 assert(sourceViewId >= 0); 2325 sourceViewShell = SfxViewShell::GetFirst( false, 2326 [sourceViewId](const SfxViewShell& shell) { return shell.GetViewShellId().get() == sourceViewId; } ); 2327 } 2328 if(sourceViewShell == nullptr) 2329 { 2330 SAL_INFO("lok", "View #" << sourceViewId << " no longer found for updated event [" << type << "]"); 2331 continue; // View removed, probably cleaning up. 2332 } 2333 enqueueUpdatedType( type, sourceViewShell, viewId ); 2334 } 2335 } 2336 } 2337 } 2338 2339 void CallbackFlushHandler::enqueueUpdatedType( int type, const SfxViewShell* viewShell, int viewId ) 2340 { 2341 if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR) 2342 { 2343 if (const SfxViewShell* viewShell2 = LokStarMathHelper(viewShell).GetSmViewShell()) 2344 viewShell = viewShell2; 2345 } 2346 std::optional<OString> payload = viewShell->getLOKPayload( type, viewId ); 2347 if(!payload) 2348 return; // No actual payload to send. 2349 CallbackData callbackData(*payload, viewId); 2350 m_queue1.emplace_back(type); 2351 m_queue2.emplace_back(callbackData); 2352 SAL_INFO("lok", "Queued updated [" << type << "]: [" << callbackData.getPayload() 2353 << "] to have " << m_queue1.size() << " entries."); 2354 } 2355 2356 void CallbackFlushHandler::Invoke() 2357 { 2358 comphelper::ProfileZone aZone("CallbackFlushHandler::Invoke"); 2359 2360 if (!m_pCallback) 2361 return; 2362 2363 // Get any pending invalidate tile events. This will call our callbacks, 2364 // so it must be done before taking the mutex. 2365 assert(m_viewId >= 0); 2366 if(SfxViewShell* viewShell = SfxViewShell::GetFirst( false, 2367 [this](const SfxViewShell& shell) { return shell.GetViewShellId().get() == m_viewId; } )) 2368 { 2369 viewShell->flushPendingLOKInvalidateTiles(); 2370 } 2371 2372 std::unique_lock<std::recursive_mutex> lock(m_mutex); 2373 2374 // Append messages for updated types, fetch them only now. 2375 enqueueUpdatedTypes(); 2376 2377 SAL_INFO("lok", "Flushing " << m_queue1.size() << " elements."); 2378 auto it1 = m_queue1.begin(); 2379 auto it2 = m_queue2.begin(); 2380 for (; it1 != m_queue1.end(); ++it1, ++it2) 2381 { 2382 const int type = *it1; 2383 const auto& payload = it2->getPayload(); 2384 const int viewId = lcl_isViewCallbackType(type) ? it2->getViewId() : -1; 2385 2386 SAL_INFO("lok", "processing event: [" << type << ',' << viewId << "]: [" << payload << "]."); 2387 2388 // common code-path for events on this view: 2389 if (viewId == -1) 2390 { 2391 sal_Int32 idx; 2392 // key-value pairs 2393 if (type == LOK_CALLBACK_STATE_CHANGED && 2394 (idx = payload.indexOf('=')) != -1) 2395 { 2396 OString key = payload.copy(0, idx); 2397 OString value = payload.copy(idx+1); 2398 const auto stateIt = m_lastStateChange.find(key); 2399 if (stateIt != m_lastStateChange.end()) 2400 { 2401 // If the value didn't change, it's safe to ignore. 2402 if (stateIt->second == value) 2403 { 2404 SAL_INFO("lok", "Skipping new state duplicate: [" << type << "]: [" << payload << "]."); 2405 continue; 2406 } 2407 SAL_INFO("lok", "Replacing a state element [" << type << "]: [" << payload << "]."); 2408 stateIt->second = value; 2409 } 2410 else 2411 { 2412 SAL_INFO("lok", "Inserted a new state element: [" << type << "]: [" << payload << "]"); 2413 m_lastStateChange.emplace(key, value); 2414 } 2415 } 2416 else 2417 { 2418 const auto stateIt = m_states.find(type); 2419 if (stateIt != m_states.end()) 2420 { 2421 // If the state didn't change, it's safe to ignore. 2422 if (stateIt->second == payload) 2423 { 2424 SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "]."); 2425 continue; 2426 } 2427 stateIt->second = payload; 2428 } 2429 } 2430 } 2431 else // less common path for events relating to other views 2432 { 2433 const auto statesIt = m_viewStates.find(viewId); 2434 if (statesIt != m_viewStates.end()) 2435 { 2436 auto& states = statesIt->second; 2437 const auto stateIt = states.find(type); 2438 if (stateIt != states.end()) 2439 { 2440 // If the state didn't change, it's safe to ignore. 2441 if (stateIt->second == payload) 2442 { 2443 SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "]."); 2444 continue; 2445 } 2446 2447 SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "]."); 2448 stateIt->second = payload; 2449 } 2450 else 2451 { 2452 SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]"); 2453 states.emplace(type, payload); 2454 2455 } 2456 } 2457 } 2458 2459 m_pCallback(type, payload.getStr(), m_pData); 2460 } 2461 2462 m_queue1.clear(); 2463 m_queue2.clear(); 2464 Stop(); 2465 m_TimeoutIdle.Stop(); 2466 } 2467 2468 void CallbackFlushHandler::startTimer() 2469 { 2470 if (!IsActive()) 2471 Start(); 2472 if (!m_TimeoutIdle.IsActive()) 2473 m_TimeoutIdle.Start(); 2474 } 2475 2476 bool CallbackFlushHandler::removeAll(int type) 2477 { 2478 bool bErased = false; 2479 auto it1 = m_queue1.begin(); 2480 for(;;) 2481 { 2482 it1 = std::find(it1, m_queue1.end(), type); 2483 if(it1 == m_queue1.end()) 2484 break; 2485 m_queue2.erase(toQueue2(it1)); 2486 it1 = m_queue1.erase(it1); 2487 bErased = true; 2488 } 2489 return bErased; 2490 } 2491 2492 bool CallbackFlushHandler::removeAll(int type, const std::function<bool (const CallbackData&)>& rTestFunc) 2493 { 2494 bool bErased = false; 2495 auto it1 = m_queue1.begin(); 2496 for(;;) 2497 { 2498 it1 = std::find(it1, m_queue1.end(), type); 2499 if(it1 == m_queue1.end()) 2500 break; 2501 auto it2 = toQueue2(it1); 2502 if (rTestFunc(*it2)) 2503 { 2504 m_queue2.erase(it2); 2505 it1 = m_queue1.erase(it1); 2506 bErased = true; 2507 } 2508 else 2509 ++it1; 2510 } 2511 return bErased; 2512 } 2513 2514 void CallbackFlushHandler::addViewStates(int viewId) 2515 { 2516 const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type()); 2517 if (!result.second && result.first != m_viewStates.end()) 2518 { 2519 result.first->second.clear(); 2520 } 2521 } 2522 2523 void CallbackFlushHandler::removeViewStates(int viewId) 2524 { 2525 m_viewStates.erase(viewId); 2526 } 2527 2528 2529 static void doc_destroy(LibreOfficeKitDocument *pThis) 2530 { 2531 comphelper::ProfileZone aZone("doc_destroy"); 2532 2533 SolarMutexGuard aGuard; 2534 2535 #ifndef IOS 2536 LOKClipboardFactory::releaseClipboardForView(-1); 2537 #endif 2538 2539 LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis); 2540 delete pDocument; 2541 } 2542 2543 static void lo_destroy (LibreOfficeKit* pThis); 2544 static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath); 2545 static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL); 2546 static char * lo_getError (LibreOfficeKit* pThis); 2547 static void lo_freeError (char* pFree); 2548 static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis, 2549 const char* pURL, 2550 const char* pOptions); 2551 static void lo_registerCallback (LibreOfficeKit* pThis, 2552 LibreOfficeKitCallback pCallback, 2553 void* pData); 2554 static char* lo_getFilterTypes(LibreOfficeKit* pThis); 2555 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features); 2556 static void lo_setDocumentPassword(LibreOfficeKit* pThis, 2557 const char* pURL, 2558 const char* pPassword); 2559 static char* lo_getVersionInfo(LibreOfficeKit* pThis); 2560 static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL); 2561 2562 static bool lo_signDocument(LibreOfficeKit* pThis, 2563 const char* pUrl, 2564 const unsigned char* pCertificateBinary, 2565 const int nCertificateBinarySize, 2566 const unsigned char* pPrivateKeyBinary, 2567 const int nPrivateKeyBinarySize); 2568 2569 static char* lo_extractRequest(LibreOfficeKit* pThis, 2570 const char* pFilePath); 2571 2572 static void lo_trimMemory(LibreOfficeKit* pThis, int nTarget); 2573 2574 static void* 2575 lo_startURP(LibreOfficeKit* pThis, void* pReceiveURPFromLOContext, void* pSendURPToLOContext, 2576 int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen), 2577 int (*fnSendURPToLO)(void* pContext, signed char* pBuffer, int nLen)); 2578 2579 static void lo_stopURP(LibreOfficeKit* pThis, void* pSendURPToLOContext); 2580 2581 static int lo_joinThreads(LibreOfficeKit* pThis); 2582 2583 static void lo_runLoop(LibreOfficeKit* pThis, 2584 LibreOfficeKitPollCallback pPollCallback, 2585 LibreOfficeKitWakeCallback pWakeCallback, 2586 void* pData); 2587 2588 static void lo_sendDialogEvent(LibreOfficeKit* pThis, 2589 unsigned long long int nLOKWindowId, 2590 const char* pArguments); 2591 2592 static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const char* pValue); 2593 2594 static void lo_dumpState(LibreOfficeKit* pThis, const char* pOptions, char** pState); 2595 2596 LibLibreOffice_Impl::LibLibreOffice_Impl() 2597 : m_pOfficeClass( gOfficeClass.lock() ) 2598 , maThread(nullptr) 2599 , mpCallback(nullptr) 2600 , mpCallbackData(nullptr) 2601 , mOptionalFeatures(0) 2602 { 2603 if(!m_pOfficeClass) { 2604 m_pOfficeClass = std::make_shared<LibreOfficeKitClass>(); 2605 m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass); 2606 2607 m_pOfficeClass->destroy = lo_destroy; 2608 m_pOfficeClass->documentLoad = lo_documentLoad; 2609 m_pOfficeClass->getError = lo_getError; 2610 m_pOfficeClass->freeError = lo_freeError; 2611 m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions; 2612 m_pOfficeClass->registerCallback = lo_registerCallback; 2613 m_pOfficeClass->getFilterTypes = lo_getFilterTypes; 2614 m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures; 2615 m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword; 2616 m_pOfficeClass->getVersionInfo = lo_getVersionInfo; 2617 m_pOfficeClass->runMacro = lo_runMacro; 2618 m_pOfficeClass->signDocument = lo_signDocument; 2619 m_pOfficeClass->runLoop = lo_runLoop; 2620 m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent; 2621 m_pOfficeClass->setOption = lo_setOption; 2622 m_pOfficeClass->dumpState = lo_dumpState; 2623 m_pOfficeClass->extractRequest = lo_extractRequest; 2624 m_pOfficeClass->trimMemory = lo_trimMemory; 2625 m_pOfficeClass->startURP = lo_startURP; 2626 m_pOfficeClass->stopURP = lo_stopURP; 2627 m_pOfficeClass->joinThreads = lo_joinThreads; 2628 2629 gOfficeClass = m_pOfficeClass; 2630 } 2631 2632 pClass = m_pOfficeClass.get(); 2633 } 2634 2635 LibLibreOffice_Impl::~LibLibreOffice_Impl() 2636 { 2637 } 2638 2639 namespace 2640 { 2641 2642 void setLanguageAndLocale(OUString const & aLangISO) 2643 { 2644 SvtSysLocaleOptions aLocalOptions; 2645 aLocalOptions.SetLocaleConfigString(aLangISO); 2646 aLocalOptions.SetUILocaleConfigString(aLangISO); 2647 aLocalOptions.Commit(); 2648 } 2649 2650 void setFormatSpecificFilterData(std::u16string_view sFormat, comphelper::SequenceAsHashMap & rFilterDataMap) 2651 { 2652 if (sFormat == u"pdf") 2653 { 2654 // always export bookmarks, which is needed for annotations 2655 rFilterDataMap[u"ExportBookmarks"_ustr] <<= true; 2656 } 2657 } 2658 2659 } // anonymous namespace 2660 2661 // Wonder global state ... 2662 static uno::Reference<css::uno::XComponentContext> xContext; 2663 static uno::Reference<css::lang::XMultiServiceFactory> xSFactory; 2664 static uno::Reference<css::lang::XMultiComponentFactory> xFactory; 2665 2666 static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL) 2667 { 2668 return lo_documentLoadWithOptions(pThis, pURL, nullptr); 2669 } 2670 2671 static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions) 2672 { 2673 comphelper::ProfileZone aZone("lo_documentLoadWithOptions"); 2674 2675 SolarMutexGuard aGuard; 2676 2677 static int nDocumentIdCounter = 0; 2678 2679 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 2680 pLib->maLastExceptionMsg.clear(); 2681 2682 const OUString aURL(getAbsoluteURL(pURL)); 2683 if (aURL.isEmpty()) 2684 { 2685 pLib->maLastExceptionMsg = u"Filename to load was not provided."_ustr; 2686 SAL_INFO("lok", "URL for load is empty"); 2687 return nullptr; 2688 } 2689 2690 pLib->maLastExceptionMsg.clear(); 2691 2692 if (!xContext.is()) 2693 { 2694 pLib->maLastExceptionMsg = u"ComponentContext is not available"_ustr; 2695 SAL_INFO("lok", "ComponentContext is not available"); 2696 return nullptr; 2697 } 2698 2699 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext); 2700 2701 if (!xComponentLoader.is()) 2702 { 2703 pLib->maLastExceptionMsg = u"ComponentLoader is not available"_ustr; 2704 SAL_INFO("lok", "ComponentLoader is not available"); 2705 return nullptr; 2706 } 2707 2708 try 2709 { 2710 // 'Language=...' is an option that LOK consumes by itself, and does 2711 // not pass it as a parameter to the filter 2712 OUString aOptions = getUString(pOptions); 2713 const OUString aLanguage = extractParameter(aOptions, u"Language"); 2714 2715 if (!aLanguage.isEmpty() && LanguageTag::isValidBcp47(aLanguage, nullptr)) 2716 { 2717 static bool isLoading = true; 2718 if (isLoading) 2719 { 2720 // Capture the language used to load the document. 2721 SfxLokHelper::setLoadLanguage(aLanguage); 2722 isLoading = false; 2723 } 2724 2725 SfxLokHelper::setDefaultLanguage(aLanguage); 2726 // Set the LOK language tag, used for dialog tunneling. 2727 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage)); 2728 comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage)); 2729 2730 SAL_INFO("lok", "Set document language to " << aLanguage); 2731 // use with care - it sets it for the entire core, not just the 2732 // document 2733 setLanguageAndLocale(aLanguage); 2734 // Need to reset the static initialized values 2735 SvNumberFormatter::resetTheCurrencyTable(); 2736 } 2737 2738 // Set the timezone, if not empty. 2739 const OUString aTimezone = extractParameter(aOptions, u"Timezone"); 2740 if (!aTimezone.isEmpty()) 2741 { 2742 SfxLokHelper::setDefaultTimezone(true, aTimezone); 2743 } 2744 else 2745 { 2746 // Default to the TZ envar, if set. 2747 const char* tz = ::getenv("TZ"); 2748 if (tz) 2749 { 2750 SfxLokHelper::setDefaultTimezone(true, 2751 OStringToOUString(tz, RTL_TEXTENCODING_UTF8)); 2752 } 2753 else 2754 { 2755 SfxLokHelper::setDefaultTimezone(false, OUString()); 2756 } 2757 } 2758 2759 const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor"); 2760 SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor); 2761 2762 const OUString aBatch = extractParameter(aOptions, u"Batch"); 2763 if (!aBatch.isEmpty()) 2764 { 2765 Application::SetDialogCancelMode(DialogCancelMode::LOKSilent); 2766 } 2767 2768 rtl::Reference<LOKInteractionHandler> const pInteraction( 2769 new LOKInteractionHandler("load"_ostr, pLib)); 2770 auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction))); 2771 comphelper::ScopeGuard const g([&] () { 2772 if (pair.second) 2773 { 2774 pLib->mInteractionMap.erase(aURL.toUtf8()); 2775 } 2776 }); 2777 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction); 2778 2779 int nMacroSecurityLevel = 1; 2780 const OUString aMacroSecurityLevel = extractParameter(aOptions, u"MacroSecurityLevel"); 2781 if (!aMacroSecurityLevel.isEmpty()) 2782 { 2783 double nNumber; 2784 sal_uInt32 nFormat = 1; 2785 SvNumberFormatter aFormatter(::comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US); 2786 if (aFormatter.IsNumberFormat(aMacroSecurityLevel, nFormat, nNumber)) 2787 nMacroSecurityLevel = static_cast<int>(nNumber); 2788 } 2789 SvtSecurityOptions::SetMacroSecurityLevel(nMacroSecurityLevel); 2790 2791 #if defined(ANDROID) && HAVE_FEATURE_ANDROID_LOK 2792 sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG; 2793 #else 2794 const OUString aEnableMacrosExecution = extractParameter(aOptions, u"EnableMacrosExecution"); 2795 sal_Int16 nMacroExecMode = aEnableMacrosExecution == "true" ? document::MacroExecMode::USE_CONFIG : 2796 document::MacroExecMode::NEVER_EXECUTE; 2797 #endif 2798 2799 // set AsTemplate explicitly false to be able to load template files 2800 // as regular files, otherwise we cannot save them; it will try 2801 // to bring saveas dialog which cannot work with LOK case 2802 uno::Sequence<css::beans::PropertyValue> aFilterOptions{ 2803 comphelper::makePropertyValue(u"FilterOptions"_ustr, aOptions), 2804 comphelper::makePropertyValue(u"InteractionHandler"_ustr, xInteraction), 2805 comphelper::makePropertyValue(u"MacroExecutionMode"_ustr, nMacroExecMode), 2806 comphelper::makePropertyValue(u"AsTemplate"_ustr, false), 2807 comphelper::makePropertyValue(u"Silent"_ustr, !aBatch.isEmpty()) 2808 }; 2809 2810 /* TODO 2811 sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG; 2812 aFilterOptions[3].Name = "UpdateDocMode"; 2813 aFilterOptions[3].Value <<= nUpdateDoc; 2814 */ 2815 2816 OutputDevice::StartTrackingFontMappingUse(); 2817 2818 const int nThisDocumentId = nDocumentIdCounter++; 2819 SfxViewShell::SetCurrentDocId(ViewShellDocId(nThisDocumentId)); 2820 uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL( 2821 aURL, u"_blank"_ustr, 0, 2822 aFilterOptions); 2823 2824 assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail 2825 2826 if (!xComponent.is()) 2827 { 2828 pLib->maLastExceptionMsg = u"loadComponentFromURL returned an empty reference"_ustr; 2829 SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg); 2830 return nullptr; 2831 } 2832 2833 LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent, nThisDocumentId); 2834 2835 // After loading the document, its initial view is the "current" view. 2836 if (pLib->mpCallback) 2837 { 2838 int nState = doc_getSignatureState(pDocument); 2839 pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData); 2840 } 2841 2842 auto aFontMappingUseData = OutputDevice::FinishTrackingFontMappingUse(); 2843 2844 if (aFontMappingUseData.size() > 0) 2845 { 2846 SAL_INFO("lok.fontsubst", "================ Original substitutions:"); 2847 for (const auto &i : aFontMappingUseData) 2848 { 2849 SAL_INFO("lok.fontsubst", i.mOriginalFont); 2850 for (const auto &j : i.mUsedFonts) 2851 SAL_INFO("lok.fontsubst", " " << j); 2852 } 2853 } 2854 2855 // Filter out font substitutions that actually aren't any substitutions, like "Liberation 2856 // Serif" -> "Liberation Serif/Regular". If even one of the "substitutions" of a font is to 2857 // the same font, don't count that as a missing font. 2858 2859 std::erase_if 2860 (aFontMappingUseData, 2861 [](OutputDevice::FontMappingUseItem x) 2862 { 2863 // If the original font had an empty style and one of its 2864 // replacement fonts has the same family name, we assume the font is 2865 // present. The root problem here is that the code that collects 2866 // font substitutions tends to get just empty styles for the font 2867 // that is being substituted, as vcl::Font::GetStyleName() tends to 2868 // return an empty string. (Italicness is instead indicated by what 2869 // vcl::Font::GetItalic() returns and boldness by what 2870 // vcl::Font::GetWeight() returns.) 2871 2872 if (x.mOriginalFont.indexOf('/') == -1) 2873 for (const auto &j : x.mUsedFonts) 2874 if (j == x.mOriginalFont || 2875 j.startsWith(Concat2View(x.mOriginalFont + "/"))) 2876 return true; 2877 2878 return false; 2879 }); 2880 2881 // Filter out substitutions where a proprietary font has been substituted by a 2882 // metric-compatible one. Obviously this is just a heuristic and implemented only for some 2883 // well-known cases. 2884 2885 std::erase_if 2886 (aFontMappingUseData, 2887 [](OutputDevice::FontMappingUseItem x) 2888 { 2889 // Again, handle only cases where the original font does not include 2890 // a style. Unclear whether there ever will be a style part included 2891 // in the mOriginalFont. 2892 2893 if (x.mOriginalFont.indexOf('/') == -1) 2894 for (const auto &j : x.mUsedFonts) 2895 if ((x.mOriginalFont == "Arial" && 2896 j.startsWith("Liberation Sans/")) || 2897 (x.mOriginalFont == "Times New Roman" && 2898 j.startsWith("Liberation Serif/")) || 2899 (x.mOriginalFont == "Courier New" && 2900 j.startsWith("Liberation Mono/")) || 2901 (x.mOriginalFont == "Arial Narrow" && 2902 j.startsWith("Liberation Sans Narrow/")) || 2903 (x.mOriginalFont == "Cambria" && 2904 j.startsWith("Caladea/")) || 2905 (x.mOriginalFont == "Calibri" && 2906 j.startsWith("Carlito/")) || 2907 (x.mOriginalFont == "Palatino Linotype" && 2908 j.startsWith("P052/")) || 2909 // Perhaps a risky heuristic? If some glyphs from Symbol 2910 // have been mapped to ones in OpenSymbol, don't warn 2911 // that Symbol is missing. 2912 (x.mOriginalFont == "Symbol" && 2913 j.startsWith("OpenSymbol/"))) 2914 { 2915 return true; 2916 } 2917 2918 return false; 2919 }); 2920 2921 if (aFontMappingUseData.size() > 0) 2922 { 2923 SAL_INFO("lok.fontsubst", "================ Pruned substitutions:"); 2924 for (const auto &i : aFontMappingUseData) 2925 { 2926 SAL_INFO("lok.fontsubst", i.mOriginalFont); 2927 for (const auto &j : i.mUsedFonts) 2928 SAL_INFO("lok.fontsubst", " " << j); 2929 } 2930 } 2931 2932 for (std::size_t i = 0; i < aFontMappingUseData.size(); ++i) 2933 { 2934 pDocument->maFontsMissing.insert(aFontMappingUseData[i].mOriginalFont); 2935 } 2936 2937 return pDocument; 2938 } 2939 catch (const uno::Exception& exception) 2940 { 2941 pLib->maLastExceptionMsg = exception.Message; 2942 TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded"); 2943 } 2944 2945 return nullptr; 2946 } 2947 2948 static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL) 2949 { 2950 comphelper::ProfileZone aZone("lo_runMacro"); 2951 2952 SolarMutexGuard aGuard; 2953 2954 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 2955 pLib->maLastExceptionMsg.clear(); 2956 2957 OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 ); 2958 if (sURL.isEmpty()) 2959 { 2960 pLib->maLastExceptionMsg = u"Macro to run was not provided."_ustr; 2961 SAL_INFO("lok", "Macro URL is empty"); 2962 return false; 2963 } 2964 2965 if (!sURL.startsWith("macro://")) 2966 { 2967 pLib->maLastExceptionMsg = u"This doesn't look like macro URL"_ustr; 2968 SAL_INFO("lok", "Macro URL is invalid"); 2969 return false; 2970 } 2971 2972 pLib->maLastExceptionMsg.clear(); 2973 2974 if (!xContext.is()) 2975 { 2976 pLib->maLastExceptionMsg = u"ComponentContext is not available"_ustr; 2977 SAL_INFO("lok", "ComponentContext is not available"); 2978 return false; 2979 } 2980 2981 util::URL aURL; 2982 aURL.Complete = sURL; 2983 2984 uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) ); 2985 2986 if( xParser.is() ) 2987 xParser->parseStrict( aURL ); 2988 2989 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext); 2990 2991 if (!xComponentLoader.is()) 2992 { 2993 pLib->maLastExceptionMsg = u"ComponentLoader is not available"_ustr; 2994 SAL_INFO("lok", "ComponentLoader is not available"); 2995 return false; 2996 } 2997 2998 xFactory = xContext->getServiceManager(); 2999 3000 if (!xFactory) 3001 return false; 3002 3003 uno::Reference<frame::XDispatchProvider> xDP; 3004 xSFactory.set(xFactory, uno::UNO_QUERY_THROW); 3005 xDP.set( xSFactory->createInstance(u"com.sun.star.comp.sfx2.SfxMacroLoader"_ustr), uno::UNO_QUERY ); 3006 uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0); 3007 3008 if (!xD.is()) 3009 { 3010 pLib->maLastExceptionMsg = u"Macro loader is not available"_ustr; 3011 SAL_INFO("lok", "Macro loader is not available"); 3012 return false; 3013 } 3014 3015 uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW ); 3016 uno::Sequence<css::beans::PropertyValue> aEmpty; 3017 css::beans::PropertyValue aErr; 3018 uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty ); 3019 aRet >>= aErr; 3020 3021 if (aErr.Name == "ErrorCode") 3022 { 3023 sal_uInt32 nErrCode = 0; // ERRCODE_NONE 3024 aErr.Value >>= nErrCode; 3025 3026 pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")"; 3027 SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode); 3028 3029 return false; 3030 } 3031 3032 return true; 3033 } 3034 3035 static bool lo_signDocument(LibreOfficeKit* /*pThis*/, 3036 const char* pURL, 3037 const unsigned char* pCertificateBinary, 3038 const int nCertificateBinarySize, 3039 const unsigned char* pPrivateKeyBinary, 3040 const int nPrivateKeyBinarySize) 3041 { 3042 comphelper::ProfileZone aZone("lo_signDocument"); 3043 3044 OUString aURL(getAbsoluteURL(pURL)); 3045 if (aURL.isEmpty()) 3046 return false; 3047 3048 if (!xContext.is()) 3049 return false; 3050 3051 uno::Sequence<sal_Int8> aCertificateSequence; 3052 3053 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize); 3054 std::string aCertificateBase64String = extractCertificate(aCertificateString); 3055 if (!aCertificateBase64String.empty()) 3056 { 3057 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String); 3058 comphelper::Base64::decode(aCertificateSequence, aBase64OUString); 3059 } 3060 else 3061 { 3062 aCertificateSequence.realloc(nCertificateBinarySize); 3063 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray()); 3064 } 3065 3066 uno::Sequence<sal_Int8> aPrivateKeySequence; 3067 std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize); 3068 std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString); 3069 if (!aPrivateKeyBase64String.empty()) 3070 { 3071 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String); 3072 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString); 3073 } 3074 else 3075 { 3076 aPrivateKeySequence.realloc(nPrivateKeyBinarySize); 3077 std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.getArray()); 3078 } 3079 3080 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext); 3081 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString()); 3082 if (!xSecurityContext.is()) 3083 return false; 3084 3085 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); 3086 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY); 3087 3088 if (!xCertificateCreator.is()) 3089 return false; 3090 3091 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence); 3092 3093 if (!xCertificate.is()) 3094 return false; 3095 3096 sfx2::DocumentSigner aDocumentSigner(aURL); 3097 if (!aDocumentSigner.signDocument(xCertificate)) 3098 return false; 3099 3100 return true; 3101 } 3102 3103 3104 static char* lo_extractRequest(LibreOfficeKit* /*pThis*/, const char* pFilePath) 3105 { 3106 uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext); 3107 uno::Reference< css::lang::XComponent > xComp; 3108 OUString aURL(getAbsoluteURL(pFilePath)); 3109 if (!aURL.isEmpty()) 3110 { 3111 if (xComponentLoader.is()) 3112 { 3113 try 3114 { 3115 uno::Sequence<css::beans::PropertyValue> aFilterOptions(comphelper::InitPropertySequence( 3116 { 3117 {u"Hidden"_ustr, css::uno::Any(true)}, 3118 {u"ReadOnly"_ustr, css::uno::Any(true)} 3119 })); 3120 xComp = xComponentLoader->loadComponentFromURL( aURL, u"_blank"_ustr, 0, aFilterOptions ); 3121 } 3122 catch ( const lang::IllegalArgumentException& ex ) 3123 { 3124 SAL_WARN("lok", "lo_extractRequest: IllegalArgumentException: " << ex.Message); 3125 } 3126 catch (...) 3127 { 3128 SAL_WARN("lok", "lo_extractRequest: Exception on loadComponentFromURL, url= " << aURL); 3129 } 3130 3131 if (xComp.is()) 3132 { 3133 uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY ); 3134 3135 if( xLTS.is() ) 3136 { 3137 tools::JsonWriter aJson; 3138 { 3139 auto aNode = aJson.startNode("Targets"); 3140 extractLinks(xLTS->getLinks(), false, aJson); 3141 } 3142 return convertOString(aJson.finishAndGetAsOString()); 3143 } 3144 xComp->dispose(); 3145 } 3146 } 3147 } 3148 return strdup("{ }"); 3149 } 3150 3151 static void lo_trimMemory(LibreOfficeKit* /* pThis */, int nTarget) 3152 { 3153 vcl::lok::trimMemory(nTarget); 3154 3155 if (nTarget > 2000) 3156 { 3157 SolarMutexGuard aGuard; 3158 3159 // Flush all buffered VOC primitives from the pages. 3160 SfxViewShell* pViewShell = SfxViewShell::Current(); 3161 if (pViewShell) 3162 { 3163 const SdrView* pView = pViewShell->GetDrawView(); 3164 if (pView) 3165 { 3166 SdrPageView* pPageView = pView->GetSdrPageView(); 3167 if (pPageView) 3168 { 3169 SdrPage* pCurPage = pPageView->GetPage(); 3170 if (pCurPage) 3171 { 3172 SdrModel& sdrModel = pCurPage->getSdrModelFromSdrPage(); 3173 for (sal_uInt16 i = 0; i < sdrModel.GetPageCount(); ++i) 3174 { 3175 SdrPage* pPage = sdrModel.GetPage(i); 3176 if (pPage) 3177 pPage->GetViewContact().flushViewObjectContacts(); 3178 } 3179 } 3180 } 3181 } 3182 } 3183 } 3184 3185 if (nTarget > 1000) 3186 { 3187 #ifdef HAVE_MALLOC_TRIM 3188 malloc_trim(0); 3189 #endif 3190 } 3191 } 3192 3193 namespace 3194 { 3195 class FunctionBasedURPInstanceProvider 3196 : public ::cppu::WeakImplHelper<css::bridge::XInstanceProvider> 3197 { 3198 private: 3199 css::uno::Reference<css::uno::XComponentContext> m_rContext; 3200 3201 public: 3202 FunctionBasedURPInstanceProvider( 3203 const css::uno::Reference<css::uno::XComponentContext>& rxContext); 3204 3205 // XInstanceProvider 3206 virtual css::uno::Reference<css::uno::XInterface> 3207 SAL_CALL getInstance(const OUString& aName) override; 3208 }; 3209 3210 // InstanceProvider 3211 FunctionBasedURPInstanceProvider::FunctionBasedURPInstanceProvider( 3212 const Reference<XComponentContext>& rxContext) 3213 : m_rContext(rxContext) 3214 { 3215 } 3216 3217 Reference<XInterface> FunctionBasedURPInstanceProvider::getInstance(const OUString& aName) 3218 { 3219 Reference<XInterface> rInstance; 3220 3221 if (aName == "StarOffice.ServiceManager") 3222 { 3223 rInstance.set(m_rContext->getServiceManager()); 3224 } 3225 else if (aName == "StarOffice.ComponentContext") 3226 { 3227 rInstance = m_rContext; 3228 } 3229 else if (aName == "StarOffice.NamingService") 3230 { 3231 Reference<XNamingService> rNamingService( 3232 m_rContext->getServiceManager()->createInstanceWithContext( 3233 u"com.sun.star.uno.NamingService"_ustr, m_rContext), 3234 UNO_QUERY); 3235 if (rNamingService.is()) 3236 { 3237 rNamingService->registerObject(u"StarOffice.ServiceManager"_ustr, 3238 m_rContext->getServiceManager()); 3239 rNamingService->registerObject(u"StarOffice.ComponentContext"_ustr, m_rContext); 3240 rInstance = rNamingService; 3241 } 3242 } 3243 return rInstance; 3244 } 3245 3246 class FunctionBasedURPConnection : public cppu::WeakImplHelper<css::connection::XConnection> 3247 { 3248 public: 3249 explicit FunctionBasedURPConnection(void*, int (*)(void* pContext, const signed char* pBuffer, int nLen), 3250 void*, int (*)(void* pContext, signed char* pBuffer, int nLen)); 3251 ~FunctionBasedURPConnection(); 3252 3253 // These overridden member functions use "read" and "write" from the point of view of LO, 3254 // i.e. the opposite to how startURP() uses them. 3255 virtual sal_Int32 SAL_CALL read(Sequence<sal_Int8>& rReadBytes, 3256 sal_Int32 nBytesToRead) override; 3257 virtual void SAL_CALL write(const Sequence<sal_Int8>& aData) override; 3258 virtual void SAL_CALL flush() override; 3259 virtual void SAL_CALL close() override; 3260 virtual OUString SAL_CALL getDescription() override; 3261 void setBridge(const Reference<XBridge>&); 3262 void* getContext(); 3263 inline static int g_connectionCount = 0; 3264 3265 private: 3266 void* m_pRecieveFromLOContext; 3267 void* m_pSendURPToLOContext; 3268 int (*m_fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen); 3269 int (*m_fnSendURPToLO)(void* pContext, signed char* pBuffer, int nLen); 3270 Reference<XBridge> m_URPBridge; 3271 }; 3272 3273 FunctionBasedURPConnection::FunctionBasedURPConnection( 3274 void* pRecieveFromLOContext, 3275 int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen), 3276 void* pSendURPToLOContext, 3277 int (*fnSendURPToLO)(void* pContext, signed char* pBuffer, int nLen)) 3278 : m_pRecieveFromLOContext(pRecieveFromLOContext) 3279 , m_pSendURPToLOContext(pSendURPToLOContext) 3280 , m_fnReceiveURPFromLO(fnReceiveURPFromLO) 3281 , m_fnSendURPToLO(fnSendURPToLO) 3282 { 3283 g_connectionCount++; 3284 } 3285 3286 FunctionBasedURPConnection::~FunctionBasedURPConnection() 3287 { 3288 Reference<XComponent> xComp(m_URPBridge, UNO_QUERY_THROW); 3289 xComp->dispose(); // TODO: check this doesn't deadlock 3290 } 3291 3292 void* FunctionBasedURPConnection::getContext() { return this; } 3293 3294 sal_Int32 FunctionBasedURPConnection::read(Sequence<sal_Int8>& rReadBytes, sal_Int32 nBytesToRead) 3295 { 3296 if (nBytesToRead < 0) 3297 return 0; 3298 3299 if (rReadBytes.getLength() != nBytesToRead) 3300 rReadBytes.realloc(nBytesToRead); 3301 3302 // As with osl::StreamPipe, we must always read nBytesToRead... 3303 return m_fnSendURPToLO(m_pSendURPToLOContext, rReadBytes.getArray(), nBytesToRead); 3304 } 3305 3306 void FunctionBasedURPConnection::write(const Sequence<sal_Int8>& rData) 3307 { 3308 m_fnReceiveURPFromLO(m_pRecieveFromLOContext, rData.getConstArray(), rData.getLength()); 3309 } 3310 3311 void FunctionBasedURPConnection::flush() {} 3312 3313 void FunctionBasedURPConnection::close() 3314 { 3315 SAL_INFO("lok.urp", "Requested to close FunctionBasedURPConnection"); 3316 } 3317 3318 OUString FunctionBasedURPConnection::getDescription() { return ""; } 3319 3320 void FunctionBasedURPConnection::setBridge(const Reference<XBridge>& xBridge) { m_URPBridge = xBridge; } 3321 } 3322 3323 static void* 3324 lo_startURP(LibreOfficeKit* /* pThis */, void* pRecieveFromLOContext, void* pSendToLOContext, 3325 int (*fnReceiveURPFromLO)(void* pContext, const signed char* pBuffer, int nLen), 3326 int (*fnSendURPToLO)(void* pContext, signed char* pBuffer, int nLen)) 3327 { 3328 // Here we will roughly do what desktop LO does when one passes a command-line switch like 3329 // --accept=socket,port=nnnn;urp;StarOffice.ServiceManager. Except that no listening socket will 3330 // be created. The communication to the URP will be through the nReceiveURPFromLO and nSendURPToLO 3331 // functions. 3332 3333 rtl::Reference<FunctionBasedURPConnection> connection( 3334 new FunctionBasedURPConnection(pRecieveFromLOContext, fnReceiveURPFromLO, 3335 pSendToLOContext, fnSendURPToLO)); 3336 3337 Reference<XBridgeFactory> xBridgeFactory = css::bridge::BridgeFactory::create(xContext); 3338 3339 Reference<XInstanceProvider> xInstanceProvider(new FunctionBasedURPInstanceProvider(xContext)); 3340 3341 Reference<XBridge> xBridge(xBridgeFactory->createBridge( 3342 "functionurp" + OUString::number(FunctionBasedURPConnection::g_connectionCount), u"urp"_ustr, 3343 connection, xInstanceProvider)); 3344 3345 connection->setBridge(std::move(xBridge)); 3346 3347 return connection->getContext(); 3348 } 3349 3350 /** 3351 * Stop a function based URP connection that you started with lo_startURP above 3352 * 3353 * @param pSendToLOContext a pointer to the context returned by lo_startURP */ 3354 static void lo_stopURP(LibreOfficeKit* /* pThis */, 3355 void* pFunctionBasedURPConnection/* FunctionBasedURPConnection* */) 3356 { 3357 static_cast<FunctionBasedURPConnection*>(pFunctionBasedURPConnection)->close(); 3358 } 3359 3360 3361 static int lo_joinThreads(LibreOfficeKit* /* pThis */) 3362 { 3363 comphelper::ThreadPool &pool = comphelper::ThreadPool::getSharedOptimalPool(); 3364 pool.joinThreadsIfIdle(); 3365 3366 // if (comphelper::getWorkerCount() > 0) 3367 // return 0; 3368 3369 // Grammar checker thread 3370 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = 3371 css::linguistic2::LinguServiceManager::create(xContext); 3372 3373 auto joinable = dynamic_cast<comphelper::LibreOfficeKit::ThreadJoinable *>(xLangSrv.get()); 3374 if (joinable && !joinable->joinThreads()) 3375 return 0; 3376 3377 return 1; 3378 } 3379 3380 static void lo_registerCallback (LibreOfficeKit* pThis, 3381 LibreOfficeKitCallback pCallback, 3382 void* pData) 3383 { 3384 SolarMutexGuard aGuard; 3385 3386 Application* pApp = GetpApp(); 3387 assert(pApp); 3388 3389 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 3390 pLib->maLastExceptionMsg.clear(); 3391 3392 pApp->m_pCallback = pLib->mpCallback = pCallback; 3393 pApp->m_pCallbackData = pLib->mpCallbackData = pData; 3394 } 3395 3396 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions) 3397 { 3398 comphelper::ProfileZone aZone("doc_saveAs"); 3399 3400 SolarMutexGuard aGuard; 3401 SetLastExceptionMsg(); 3402 3403 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 3404 3405 OUString sFormat = getUString(pFormat); 3406 OUString aURL(getAbsoluteURL(sUrl)); 3407 3408 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW); 3409 3410 if (aURL.isEmpty()) 3411 { 3412 SetLastExceptionMsg(u"Filename to save to was not provided."_ustr); 3413 SAL_INFO("lok", "URL for save is empty"); 3414 return false; 3415 } 3416 3417 try 3418 { 3419 std::span<const ExtensionMap> pMap; 3420 3421 switch (doc_getDocumentType(pThis)) 3422 { 3423 case LOK_DOCTYPE_SPREADSHEET: 3424 pMap = aCalcExtensionMap; 3425 break; 3426 case LOK_DOCTYPE_PRESENTATION: 3427 pMap = aImpressExtensionMap; 3428 break; 3429 case LOK_DOCTYPE_DRAWING: 3430 pMap = aDrawExtensionMap; 3431 break; 3432 case LOK_DOCTYPE_TEXT: 3433 pMap = aWriterExtensionMap; 3434 break; 3435 case LOK_DOCTYPE_OTHER: 3436 default: 3437 SAL_INFO("lok", "Can't save document - unsupported document type."); 3438 return false; 3439 } 3440 3441 if (pFormat == nullptr) 3442 { 3443 // sniff from the extension 3444 sal_Int32 idx = aURL.lastIndexOf("."); 3445 if( idx > 0 ) 3446 { 3447 sFormat = aURL.copy( idx + 1 ); 3448 } 3449 else 3450 { 3451 SetLastExceptionMsg("input URL '" + aURL + "' lacks a suffix"); 3452 return false; 3453 } 3454 } 3455 3456 OUString aFilterName; 3457 for (const auto& item : pMap) 3458 { 3459 if (sFormat.equalsIgnoreAsciiCaseAscii(item.extn)) 3460 { 3461 aFilterName = item.filterName; 3462 break; 3463 } 3464 } 3465 if (aFilterName.isEmpty()) 3466 { 3467 SetLastExceptionMsg(u"no output filter found for provided suffix"_ustr); 3468 return false; 3469 } 3470 3471 OUString aFilterOptions = getUString(pFilterOptions); 3472 3473 // Check if watermark for pdf is passed by filteroptions... 3474 // It is not a real filter option so it must be filtered out. 3475 OUString watermarkText; 3476 std::u16string_view sFullSheetPreview; 3477 int aIndex = -1; 3478 if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0) 3479 { 3480 int bIndex = aFilterOptions.indexOf("WATERMARKEND"); 3481 watermarkText = aFilterOptions.subView(aIndex+11, bIndex-(aIndex+11)); 3482 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+12); 3483 } 3484 3485 if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0) 3486 { 3487 int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND"); 3488 sFullSheetPreview = aFilterOptions.subView(aIndex+18, bIndex-(aIndex+18)); 3489 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+16); 3490 } 3491 3492 bool bFullSheetPreview = sFullSheetPreview == u"true"; 3493 3494 OUString filePassword; 3495 if ((aIndex = aFilterOptions.indexOf(",Password=")) >= 0) 3496 { 3497 int bIndex = aFilterOptions.indexOf("PASSWORDEND"); 3498 filePassword = aFilterOptions.subView(aIndex + 10, bIndex - (aIndex + 10)); 3499 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) 3500 + aFilterOptions.subView(bIndex + 11); 3501 } 3502 OUString filePasswordToModify; 3503 if ((aIndex = aFilterOptions.indexOf(",PasswordToModify=")) >= 0) 3504 { 3505 int bIndex = aFilterOptions.indexOf("PASSWORDTOMODIFYEND"); 3506 filePassword = aFilterOptions.subView(aIndex + 18, bIndex - (aIndex + 18)); 3507 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) 3508 + aFilterOptions.subView(bIndex + 19); 3509 } 3510 3511 // Select a pdf version if specified a valid one. If not specified then ignore. 3512 // If invalid then fail. 3513 sal_Int32 pdfVer = 0; 3514 if ((aIndex = aFilterOptions.indexOf(",PDFVer=")) >= 0) 3515 { 3516 int bIndex = aFilterOptions.indexOf("PDFVEREND"); 3517 std::u16string_view sPdfVer = aFilterOptions.subView(aIndex+8, bIndex-(aIndex+8)); 3518 aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+9); 3519 3520 if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-1b")) 3521 pdfVer = 1; 3522 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-2b")) 3523 pdfVer = 2; 3524 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF/A-3b")) 3525 pdfVer = 3; 3526 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.5")) 3527 pdfVer = 15; 3528 else if (o3tl::equalsIgnoreAsciiCase(sPdfVer, u"PDF-1.6")) 3529 pdfVer = 16; 3530 else 3531 { 3532 SetLastExceptionMsg(u"wrong PDF version"_ustr); 3533 return false; 3534 } 3535 } 3536 3537 // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document 3538 // gets a new name). When this is not provided, the meaning of 3539 // saveAs() is more like save-a-copy, which allows saving to any 3540 // random format like PDF or PNG. 3541 // It is not a real filter option, so we have to filter it out. 3542 const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions); 3543 std::vector<OUString> aFilteredOptionVec; 3544 bool bTakeOwnership = false; 3545 MediaDescriptor aSaveMediaDescriptor; 3546 for (const auto& rOption : aOptionSeq) 3547 { 3548 if (rOption == "TakeOwnership") 3549 bTakeOwnership = true; 3550 else if (rOption == "NoFileSync") 3551 aSaveMediaDescriptor[u"NoFileSync"_ustr] <<= true; 3552 else 3553 aFilteredOptionVec.push_back(rOption); 3554 } 3555 3556 aSaveMediaDescriptor[u"Overwrite"_ustr] <<= true; 3557 aSaveMediaDescriptor[u"FilterName"_ustr] <<= aFilterName; 3558 3559 auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec); 3560 aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq); 3561 aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS] <<= aFilterOptions; 3562 3563 comphelper::SequenceAsHashMap aFilterDataMap; 3564 3565 // If filter options is JSON string, then make sure aFilterDataMap stays empty, otherwise we 3566 // would ignore the filter options. 3567 if (!aFilterOptions.startsWith("{")) 3568 { 3569 setFormatSpecificFilterData(sFormat, aFilterDataMap); 3570 } 3571 3572 if (!watermarkText.isEmpty()) 3573 aFilterDataMap[u"TiledWatermark"_ustr] <<= watermarkText; 3574 3575 if (bFullSheetPreview) 3576 aFilterDataMap[u"SinglePageSheets"_ustr] <<= true; 3577 3578 if (pdfVer) 3579 aFilterDataMap[u"SelectPdfVersion"_ustr] <<= pdfVer; 3580 3581 if (!aFilterDataMap.empty()) 3582 { 3583 aSaveMediaDescriptor[u"FilterData"_ustr] <<= aFilterDataMap.getAsConstPropertyValueList(); 3584 } 3585 if (!filePassword.isEmpty()) 3586 aSaveMediaDescriptor[u"Password"_ustr] <<= filePassword; 3587 if (!filePasswordToModify.isEmpty()) 3588 aSaveMediaDescriptor[u"PasswordToModify"_ustr] <<= filePasswordToModify; 3589 3590 // add interaction handler too 3591 if (gImpl) 3592 { 3593 // gImpl does not have to exist when running from a unit test 3594 rtl::Reference<LOKInteractionHandler> const pInteraction( 3595 new LOKInteractionHandler("saveas"_ostr, gImpl, pDocument)); 3596 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction); 3597 3598 aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER] <<= xInteraction; 3599 } 3600 3601 3602 if (bTakeOwnership) 3603 xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList()); 3604 else 3605 xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList()); 3606 3607 return true; 3608 } 3609 catch (const uno::Exception& exception) 3610 { 3611 SetLastExceptionMsg("exception: " + exception.Message); 3612 } 3613 return false; 3614 } 3615 3616 /** 3617 * Initialize UNO commands, in the sense that from now on, the LOK client gets updates for status 3618 * changes of these commands. This is necessary, because (unlike in the desktop case) there are no 3619 * toolbars hosting widgets these UNO commands, so no such status updates would be sent to the 3620 * headless LOK clients out of the box. 3621 */ 3622 static void doc_iniUnoCommands () 3623 { 3624 SolarMutexGuard aGuard; 3625 SetLastExceptionMsg(); 3626 3627 static constexpr OUString sUnoCommands[] = 3628 { 3629 u".uno:AlignLeft"_ustr, 3630 u".uno:AlignHorizontalCenter"_ustr, 3631 u".uno:AlignRight"_ustr, 3632 u".uno:BackColor"_ustr, 3633 u".uno:BackgroundColor"_ustr, 3634 u".uno:TableCellBackgroundColor"_ustr, 3635 u".uno:Bold"_ustr, 3636 u".uno:CenterPara"_ustr, 3637 u".uno:CharBackColor"_ustr, 3638 u".uno:CharBackgroundExt"_ustr, 3639 u".uno:CharFontName"_ustr, 3640 u".uno:Color"_ustr, 3641 u".uno:ControlCodes"_ustr, 3642 u".uno:DecrementIndent"_ustr, 3643 u".uno:DefaultBullet"_ustr, 3644 u".uno:DefaultNumbering"_ustr, 3645 u".uno:FontColor"_ustr, 3646 u".uno:FontHeight"_ustr, 3647 u".uno:IncrementIndent"_ustr, 3648 u".uno:Italic"_ustr, 3649 u".uno:JustifyPara"_ustr, 3650 u".uno:JumpToMark"_ustr, 3651 u".uno:OutlineFont"_ustr, 3652 u".uno:LeftPara"_ustr, 3653 u".uno:LanguageStatus"_ustr, 3654 u".uno:RightPara"_ustr, 3655 u".uno:Shadowed"_ustr, 3656 u".uno:SubScript"_ustr, 3657 u".uno:SuperScript"_ustr, 3658 u".uno:Strikeout"_ustr, 3659 u".uno:StyleApply"_ustr, 3660 u".uno:Underline"_ustr, 3661 u".uno:ModifiedStatus"_ustr, 3662 u".uno:Undo"_ustr, 3663 u".uno:Redo"_ustr, 3664 u".uno:InsertPage"_ustr, 3665 u".uno:DeletePage"_ustr, 3666 u".uno:DuplicatePage"_ustr, 3667 u".uno:InsertSlide"_ustr, 3668 u".uno:DeleteSlide"_ustr, 3669 u".uno:DuplicateSlide"_ustr, 3670 u".uno:ChangeTheme"_ustr, 3671 u".uno:Cut"_ustr, 3672 u".uno:Copy"_ustr, 3673 u".uno:Paste"_ustr, 3674 u".uno:SelectAll"_ustr, 3675 u".uno:ReplyComment"_ustr, 3676 u".uno:ResolveComment"_ustr, 3677 u".uno:ResolveCommentThread"_ustr, 3678 u".uno:InsertRowsBefore"_ustr, 3679 u".uno:InsertRowsAfter"_ustr, 3680 u".uno:InsertColumnsBefore"_ustr, 3681 u".uno:InsertColumnsAfter"_ustr, 3682 u".uno:DeleteRows"_ustr, 3683 u".uno:DeleteColumns"_ustr, 3684 u".uno:DeleteTable"_ustr, 3685 u".uno:SelectTable"_ustr, 3686 u".uno:EntireRow"_ustr, 3687 u".uno:EntireColumn"_ustr, 3688 u".uno:EntireCell"_ustr, 3689 u".uno:AssignLayout"_ustr, 3690 u".uno:StatusDocPos"_ustr, 3691 u".uno:RowColSelCount"_ustr, 3692 u".uno:StatusPageStyle"_ustr, 3693 u".uno:InsertMode"_ustr, 3694 u".uno:SpellOnline"_ustr, 3695 u".uno:StatusSelectionMode"_ustr, 3696 u".uno:StateTableCell"_ustr, 3697 u".uno:StatusBarFunc"_ustr, 3698 u".uno:StatePageNumber"_ustr, 3699 u".uno:StateWordCount"_ustr, 3700 u".uno:SelectionMode"_ustr, 3701 u".uno:PageStatus"_ustr, 3702 u".uno:LayoutStatus"_ustr, 3703 u".uno:Scale"_ustr, 3704 u".uno:Context"_ustr, 3705 u".uno:WrapText"_ustr, 3706 u".uno:ToggleMergeCells"_ustr, 3707 u".uno:NumberFormatCurrency"_ustr, 3708 u".uno:NumberFormatPercent"_ustr, 3709 u".uno:NumberFormatDecimal"_ustr, 3710 u".uno:NumberFormatIncDecimals"_ustr, 3711 u".uno:NumberFormatDecDecimals"_ustr, 3712 u".uno:NumberFormatDate"_ustr, 3713 u".uno:EditHeaderAndFooter"_ustr, 3714 u".uno:FrameLineColor"_ustr, 3715 u".uno:SortAscending"_ustr, 3716 u".uno:SortDescending"_ustr, 3717 u".uno:TrackChanges"_ustr, 3718 u".uno:ShowTrackedChanges"_ustr, 3719 u".uno:NextTrackedChange"_ustr, 3720 u".uno:PreviousTrackedChange"_ustr, 3721 u".uno:AcceptAllTrackedChanges"_ustr, 3722 u".uno:RejectAllTrackedChanges"_ustr, 3723 u".uno:TableDialog"_ustr, 3724 u".uno:FormatCellDialog"_ustr, 3725 u".uno:FontDialog"_ustr, 3726 u".uno:ParagraphDialog"_ustr, 3727 u".uno:OutlineBullet"_ustr, 3728 u".uno:InsertIndexesEntry"_ustr, 3729 u".uno:DocumentRepair"_ustr, 3730 u".uno:TransformDialog"_ustr, 3731 u".uno:InsertPageHeader"_ustr, 3732 u".uno:InsertPageFooter"_ustr, 3733 u".uno:OnlineAutoFormat"_ustr, 3734 u".uno:InsertObjectChart"_ustr, 3735 u".uno:InsertSection"_ustr, 3736 u".uno:InsertAnnotation"_ustr, 3737 u".uno:DeleteAnnotation"_ustr, 3738 u".uno:InsertPagebreak"_ustr, 3739 u".uno:InsertColumnBreak"_ustr, 3740 u".uno:HyperlinkDialog"_ustr, 3741 u".uno:InsertSymbol"_ustr, 3742 u".uno:EditRegion"_ustr, 3743 u".uno:ThesaurusDialog"_ustr, 3744 u".uno:FormatArea"_ustr, 3745 u".uno:FormatLine"_ustr, 3746 u".uno:FormatColumns"_ustr, 3747 u".uno:Watermark"_ustr, 3748 u".uno:ResetAttributes"_ustr, 3749 u".uno:Orientation"_ustr, 3750 u".uno:ObjectAlignLeft"_ustr, 3751 u".uno:ObjectAlignRight"_ustr, 3752 u".uno:AlignCenter"_ustr, 3753 u".uno:TransformPosX"_ustr, 3754 u".uno:TransformPosY"_ustr, 3755 u".uno:TransformWidth"_ustr, 3756 u".uno:TransformHeight"_ustr, 3757 u".uno:ObjectBackOne"_ustr, 3758 u".uno:SendToBack"_ustr, 3759 u".uno:ObjectForwardOne"_ustr, 3760 u".uno:BringToFront"_ustr, 3761 u".uno:WrapRight"_ustr, 3762 u".uno:WrapThrough"_ustr, 3763 u".uno:WrapLeft"_ustr, 3764 u".uno:WrapIdeal"_ustr, 3765 u".uno:WrapOn"_ustr, 3766 u".uno:WrapOff"_ustr, 3767 u".uno:UpdateCurIndex"_ustr, 3768 u".uno:InsertCaptionDialog"_ustr, 3769 u".uno:FormatGroup"_ustr, 3770 u".uno:SplitTable"_ustr, 3771 u".uno:SplitCell"_ustr, 3772 u".uno:MergeCells"_ustr, 3773 u".uno:DeleteNote"_ustr, 3774 u".uno:AcceptChanges"_ustr, 3775 u".uno:FormatPaintbrush"_ustr, 3776 u".uno:SetDefault"_ustr, 3777 u".uno:ParaLeftToRight"_ustr, 3778 u".uno:ParaRightToLeft"_ustr, 3779 u".uno:ParaspaceIncrease"_ustr, 3780 u".uno:ParaspaceDecrease"_ustr, 3781 u".uno:AcceptTrackedChange"_ustr, 3782 u".uno:RejectTrackedChange"_ustr, 3783 u".uno:ShowResolvedAnnotations"_ustr, 3784 u".uno:InsertBreak"_ustr, 3785 u".uno:InsertEndnote"_ustr, 3786 u".uno:InsertFootnote"_ustr, 3787 u".uno:InsertReferenceField"_ustr, 3788 u".uno:InsertBookmark"_ustr, 3789 u".uno:InsertAuthoritiesEntry"_ustr, 3790 u".uno:InsertMultiIndex"_ustr, 3791 u".uno:InsertField"_ustr, 3792 u".uno:PageNumberWizard"_ustr, 3793 u".uno:InsertPageNumberField"_ustr, 3794 u".uno:InsertPageCountField"_ustr, 3795 u".uno:InsertDateField"_ustr, 3796 u".uno:InsertTitleField"_ustr, 3797 u".uno:InsertFieldCtrl"_ustr, 3798 u".uno:CharmapControl"_ustr, 3799 u".uno:EnterGroup"_ustr, 3800 u".uno:LeaveGroup"_ustr, 3801 u".uno:AlignUp"_ustr, 3802 u".uno:AlignMiddle"_ustr, 3803 u".uno:AlignDown"_ustr, 3804 u".uno:TraceChangeMode"_ustr, 3805 u".uno:Combine"_ustr, 3806 u".uno:Merge"_ustr, 3807 u".uno:Dismantle"_ustr, 3808 u".uno:Substract"_ustr, 3809 u".uno:DistributeSelection"_ustr, 3810 u".uno:Intersect"_ustr, 3811 u".uno:BorderInner"_ustr, 3812 u".uno:BorderOuter"_ustr, 3813 u".uno:FreezePanes"_ustr, 3814 u".uno:FreezePanesColumn"_ustr, 3815 u".uno:FreezePanesRow"_ustr, 3816 u".uno:Sidebar"_ustr, 3817 u".uno:SheetRightToLeft"_ustr, 3818 u".uno:RunMacro"_ustr, 3819 u".uno:SpacePara1"_ustr, 3820 u".uno:SpacePara15"_ustr, 3821 u".uno:SpacePara2"_ustr, 3822 u".uno:InsertSparkline"_ustr, 3823 u".uno:DeleteSparkline"_ustr, 3824 u".uno:DeleteSparklineGroup"_ustr, 3825 u".uno:EditSparklineGroup"_ustr, 3826 u".uno:EditSparkline"_ustr, 3827 u".uno:GroupSparklines"_ustr, 3828 u".uno:UngroupSparklines"_ustr, 3829 u".uno:FormatSparklineMenu"_ustr, 3830 u".uno:DataDataPilotRun"_ustr, 3831 u".uno:RecalcPivotTable"_ustr, 3832 u".uno:DeletePivotTable"_ustr, 3833 u".uno:Protect"_ustr, 3834 u".uno:UnsetCellsReadOnly"_ustr, 3835 u".uno:ContentControlProperties"_ustr, 3836 u".uno:InsertCheckboxContentControl"_ustr, 3837 u".uno:InsertContentControl"_ustr, 3838 u".uno:InsertDateContentControl"_ustr, 3839 u".uno:InsertDropdownContentControl"_ustr, 3840 u".uno:InsertPlainTextContentControl"_ustr, 3841 u".uno:InsertPictureContentControl"_ustr, 3842 u".uno:DataFilterAutoFilter"_ustr, 3843 u".uno:CellProtection"_ustr, 3844 u".uno:MoveKeepInsertMode"_ustr 3845 }; 3846 3847 util::URL aCommandURL; 3848 SfxViewShell* pViewShell = SfxViewShell::Current(); 3849 SfxViewFrame* pViewFrame = pViewShell ? &pViewShell->GetViewFrame() : nullptr; 3850 3851 // check if Frame-Controller were created. 3852 if (!pViewFrame) 3853 { 3854 SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created."); 3855 return; 3856 } 3857 3858 if (!xContext.is()) 3859 xContext = comphelper::getProcessComponentContext(); 3860 if (!xContext.is()) 3861 { 3862 SAL_WARN("lok", "iniUnoCommands: Component context is not available"); 3863 return; 3864 } 3865 3866 #if !defined IOS && !defined ANDROID && !defined __EMSCRIPTEN__ 3867 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext); 3868 if (!xSEInitializer.is()) 3869 { 3870 SAL_WARN("lok", "iniUnoCommands: XSEInitializer is not available"); 3871 return; 3872 } 3873 3874 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = 3875 xSEInitializer->createSecurityContext(OUString()); 3876 if (!xSecurityContext.is()) 3877 { 3878 SAL_WARN("lok", "iniUnoCommands: failed to create security context"); 3879 } 3880 #endif 3881 3882 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame); 3883 uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext)); 3884 3885 for (const auto & sUnoCommand : sUnoCommands) 3886 { 3887 aCommandURL.Complete = sUnoCommand; 3888 xParser->parseStrict(aCommandURL); 3889 3890 // when null, this command is not supported by the given component 3891 // (like eg. Calc does not have ".uno:DefaultBullet" etc.) 3892 if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path)) 3893 { 3894 // Initialize slot to dispatch .uno: Command. 3895 pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false); 3896 } 3897 } 3898 } 3899 3900 static int doc_getDocumentType (LibreOfficeKitDocument* pThis) 3901 { 3902 comphelper::ProfileZone aZone("doc_getDocumentType"); 3903 3904 SolarMutexGuard aGuard; 3905 return getDocumentType(pThis); 3906 } 3907 3908 static int doc_getParts (LibreOfficeKitDocument* pThis) 3909 { 3910 comphelper::ProfileZone aZone("doc_getParts"); 3911 3912 SolarMutexGuard aGuard; 3913 3914 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3915 if (!pDoc) 3916 { 3917 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 3918 return 0; 3919 } 3920 3921 return pDoc->getParts(); 3922 } 3923 3924 static int doc_getPart (LibreOfficeKitDocument* pThis) 3925 { 3926 comphelper::ProfileZone aZone("doc_getPart"); 3927 3928 SolarMutexGuard aGuard; 3929 SetLastExceptionMsg(); 3930 3931 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3932 if (!pDoc) 3933 { 3934 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 3935 return 0; 3936 } 3937 3938 return pDoc->getPart(); 3939 } 3940 3941 static void doc_setPartImpl(LibreOfficeKitDocument* pThis, int nPart, bool bAllowChangeFocus = true) 3942 { 3943 comphelper::ProfileZone aZone("doc_setPart"); 3944 3945 SolarMutexGuard aGuard; 3946 SetLastExceptionMsg(); 3947 3948 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3949 if (!pDoc) 3950 { 3951 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 3952 return; 3953 } 3954 3955 pDoc->setPart( nPart, bAllowChangeFocus ); 3956 } 3957 3958 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart) 3959 { 3960 doc_setPartImpl(pThis, nPart, true); 3961 } 3962 3963 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart) 3964 { 3965 comphelper::ProfileZone aZone("doc_getPartInfo"); 3966 3967 SolarMutexGuard aGuard; 3968 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3969 if (!pDoc) 3970 { 3971 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 3972 return nullptr; 3973 } 3974 3975 return convertOUString(pDoc->getPartInfo(nPart)); 3976 } 3977 3978 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect) 3979 { 3980 SolarMutexGuard aGuard; 3981 SetLastExceptionMsg(); 3982 3983 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3984 if (!pDoc) 3985 { 3986 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 3987 return; 3988 } 3989 3990 pDoc->selectPart( nPart, nSelect ); 3991 } 3992 3993 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate) 3994 { 3995 SolarMutexGuard aGuard; 3996 SetLastExceptionMsg(); 3997 3998 ITiledRenderable* pDoc = getTiledRenderable(pThis); 3999 if (!pDoc) 4000 { 4001 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4002 return; 4003 } 4004 4005 pDoc->moveSelectedParts(nPosition, bDuplicate); 4006 } 4007 4008 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis) 4009 { 4010 comphelper::ProfileZone aZone("doc_getPartPageRectangles"); 4011 4012 SolarMutexGuard aGuard; 4013 SetLastExceptionMsg(); 4014 4015 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4016 if (!pDoc) 4017 { 4018 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4019 return nullptr; 4020 } 4021 4022 return convertOUString(pDoc->getPartPageRectangles()); 4023 } 4024 4025 static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis) 4026 { 4027 SolarMutexGuard aGuard; 4028 SetLastExceptionMsg(); 4029 4030 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4031 if (!pDoc) 4032 { 4033 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4034 return nullptr; 4035 } 4036 4037 if (SfxViewShell* pViewShell = SfxViewShell::Current()) 4038 { 4039 return convertOUString(pViewShell->getA11yFocusedParagraph()); 4040 4041 } 4042 return nullptr; 4043 } 4044 4045 static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis) 4046 { 4047 SolarMutexGuard aGuard; 4048 SetLastExceptionMsg(); 4049 4050 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4051 if (!pDoc) 4052 { 4053 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4054 return -1; 4055 } 4056 if (SfxViewShell* pViewShell = SfxViewShell::Current()) 4057 { 4058 return pViewShell->getA11yCaretPosition(); 4059 4060 } 4061 return -1; 4062 4063 } 4064 4065 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart) 4066 { 4067 comphelper::ProfileZone aZone("doc_getPartName"); 4068 4069 SolarMutexGuard aGuard; 4070 SetLastExceptionMsg(); 4071 4072 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4073 if (!pDoc) 4074 { 4075 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4076 return nullptr; 4077 } 4078 4079 return convertOUString(pDoc->getPartName(nPart)); 4080 } 4081 4082 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart) 4083 { 4084 comphelper::ProfileZone aZone("doc_getPartHash"); 4085 4086 SolarMutexGuard aGuard; 4087 SetLastExceptionMsg(); 4088 4089 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4090 if (!pDoc) 4091 { 4092 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4093 return nullptr; 4094 } 4095 4096 return convertOUString(pDoc->getPartHash(nPart)); 4097 } 4098 4099 static void doc_setPartMode(LibreOfficeKitDocument* pThis, 4100 int nPartMode) 4101 { 4102 comphelper::ProfileZone aZone("doc_setPartMode"); 4103 4104 SolarMutexGuard aGuard; 4105 SetLastExceptionMsg(); 4106 4107 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4108 if (!pDoc) 4109 { 4110 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4111 return; 4112 } 4113 4114 4115 int nCurrentPart = pDoc->getPart(); 4116 4117 pDoc->setPartMode(nPartMode); 4118 4119 // We need to make sure the internal state is updated, just changing the mode 4120 // might not update the relevant shells (i.e. impress will keep rendering the 4121 // previous mode unless we do this). 4122 // TODO: we might want to do this within the relevant components rather than 4123 // here, but that's also dependent on how we implement embedded object 4124 // rendering I guess? 4125 // TODO: we could be clever and e.g. set to 0 when we change to/from 4126 // embedded object mode, and not when changing between slide/notes/combined 4127 // modes? 4128 if ( nCurrentPart < pDoc->getParts() ) 4129 { 4130 pDoc->setPart( nCurrentPart ); 4131 } 4132 else 4133 { 4134 pDoc->setPart( 0 ); 4135 } 4136 } 4137 4138 static int doc_getEditMode(LibreOfficeKitDocument* pThis) 4139 { 4140 comphelper::ProfileZone aZone("doc_getEditMode"); 4141 4142 SolarMutexGuard aGuard; 4143 SetLastExceptionMsg(); 4144 4145 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4146 if (!pDoc) 4147 { 4148 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4149 return 0; 4150 } 4151 4152 return pDoc->getEditMode(); 4153 } 4154 4155 static void doc_paintTile(LibreOfficeKitDocument* pThis, 4156 unsigned char* pBuffer, 4157 const int nCanvasWidth, const int nCanvasHeight, 4158 const int nTilePosX, const int nTilePosY, 4159 const int nTileWidth, const int nTileHeight) 4160 { 4161 comphelper::ProfileZone aZone("doc_paintTile"); 4162 4163 SolarMutexGuard aGuard; 4164 SetLastExceptionMsg(); 4165 4166 SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight << 4167 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" << 4168 nCanvasWidth << "x" << nCanvasHeight << "]px" ); 4169 4170 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4171 if (!pDoc) 4172 { 4173 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4174 return; 4175 } 4176 4177 #if defined(UNX) && !defined(MACOSX) || defined(_WIN32) 4178 4179 // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%, 4180 // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that 4181 // everything is painted bigger or smaller. This is different to what Calc's internal scaling 4182 // would do - because that one is trying to fit the lines between cells to integer multiples of 4183 // pixels. 4184 comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); }); 4185 4186 #if defined(IOS) 4187 double fDPIScale = 1.0; 4188 4189 // Onine uses the LOK_TILEMODE_RGBA by default so flip the normal flags 4190 // to kCGImageAlphaPremultipliedLast | kCGImageByteOrder32Big 4191 CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8, 4192 nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(), 4193 kCGImageAlphaPremultipliedLast | kCGImageByteOrder32Big); 4194 4195 CGContextTranslateCTM(pCGContext, 0, nCanvasHeight); 4196 CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale); 4197 4198 SAL_INFO( "lok.tiledrendering", "doc_paintTile: painting [" << nTileWidth << "x" << nTileHeight << 4199 "]@(" << nTilePosX << ", " << nTilePosY << ") to [" << 4200 nCanvasWidth << "x" << nCanvasHeight << "]px" ); 4201 4202 Size aCanvasSize(nCanvasWidth, nCanvasHeight); 4203 4204 SystemGraphicsData aData; 4205 aData.rCGContext = reinterpret_cast<CGContextRef>(pCGContext); 4206 4207 ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA); 4208 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); 4209 pDevice->SetOutputSizePixel(aCanvasSize); 4210 pDoc->paintTile(*pDevice, aCanvasSize.Width(), aCanvasSize.Height(), 4211 nTilePosX, nTilePosY, nTileWidth, nTileHeight); 4212 4213 CGContextRelease(pCGContext); 4214 #else 4215 ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::WITHOUT_ALPHA); 4216 4217 // Set background to transparent by default. 4218 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); 4219 4220 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer( 4221 Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(), 4222 pBuffer); 4223 4224 pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight, 4225 nTilePosX, nTilePosY, nTileWidth, nTileHeight); 4226 4227 static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr; 4228 if (bDebug) 4229 { 4230 // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins. 4231 tools::Rectangle aRect(0, 0, 5, 5); 4232 aRect = pDevice->PixelToLogic(aRect); 4233 pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR); 4234 pDevice->SetFillColor(COL_LIGHTRED); 4235 pDevice->SetLineColor(); 4236 pDevice->DrawRect(aRect); 4237 pDevice->Pop(); 4238 } 4239 4240 #ifdef _WIN32 4241 // pBuffer was not used there 4242 pDevice->EnableMapMode(false); 4243 BitmapEx aBmpEx = pDevice->GetBitmapEx({ 0, 0 }, { nCanvasWidth, nCanvasHeight }); 4244 Bitmap aBmp = aBmpEx.GetBitmap(); 4245 AlphaMask aAlpha = aBmpEx.GetAlphaMask(); 4246 BitmapScopedReadAccess sraBmp(aBmp); 4247 BitmapScopedReadAccess sraAlpha(aAlpha); 4248 4249 assert(sraBmp->Height() == nCanvasHeight); 4250 assert(sraBmp->Width() == nCanvasWidth); 4251 assert(!sraAlpha || sraBmp->Height() == sraAlpha->Height()); 4252 assert(!sraAlpha || sraBmp->Width() == sraAlpha->Width()); 4253 auto p = pBuffer; 4254 for (tools::Long y = 0; y < sraBmp->Height(); ++y) 4255 { 4256 Scanline dataBmp = sraBmp->GetScanline(y); 4257 Scanline dataAlpha = sraAlpha ? sraAlpha->GetScanline(y) : nullptr; 4258 for (tools::Long x = 0; x < sraBmp->Width(); ++x) 4259 { 4260 BitmapColor color = sraBmp->GetPixelFromData(dataBmp, x); 4261 sal_uInt8 alpha = dataAlpha ? sraAlpha->GetPixelFromData(dataAlpha, x).GetBlue() : 255; 4262 *p++ = color.GetBlue(); 4263 *p++ = color.GetGreen(); 4264 *p++ = color.GetRed(); 4265 *p++ = alpha; 4266 } 4267 } 4268 #endif 4269 #endif 4270 4271 #else 4272 (void) pBuffer; 4273 #endif 4274 } 4275 4276 static void doc_paintPartTile(LibreOfficeKitDocument* pThis, 4277 unsigned char* pBuffer, 4278 const int nPart, 4279 const int nMode, 4280 const int nCanvasWidth, const int nCanvasHeight, 4281 const int nTilePosX, const int nTilePosY, 4282 const int nTileWidth, const int nTileHeight) 4283 { 4284 comphelper::ProfileZone aZone("doc_paintPartTile"); 4285 4286 SolarMutexGuard aGuard; 4287 SetLastExceptionMsg(); 4288 4289 SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " : " << nMode << " [" 4290 << nTileWidth << "x" << nTileHeight << "]@(" 4291 << nTilePosX << ", " << nTilePosY << ") to [" 4292 << nCanvasWidth << "x" << nCanvasHeight << "]px" ); 4293 4294 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 4295 int nOrigViewId = doc_getView(pThis); 4296 4297 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4298 if (!pDoc) 4299 { 4300 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4301 return; 4302 } 4303 4304 if (nOrigViewId < 0) 4305 { 4306 // tile painting always needs a SfxViewShell::Current(), but actually 4307 // it does not really matter which one - all of them should paint the 4308 // same thing. It's important to get a view for the correct document, 4309 // though. 4310 // doc_getViewsCount() returns the count of views for the document in the current view. 4311 int viewCount = doc_getViewsCount(pThis); 4312 if (viewCount == 0) 4313 return; 4314 4315 std::vector<int> viewIds(viewCount); 4316 doc_getViewIds(pThis, viewIds.data(), viewCount); 4317 4318 nOrigViewId = viewIds[0]; 4319 doc_setView(pThis, nOrigViewId); 4320 } 4321 4322 // Disable callbacks while we are painting. 4323 if (nOrigViewId >= 0) 4324 { 4325 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId); 4326 if (handlerIt != pDocument->mpCallbackFlushHandlers.end()) 4327 handlerIt->second->disableCallbacks(); 4328 } 4329 4330 try 4331 { 4332 // Text documents have a single coordinate system; don't change part. 4333 int nOrigPart = 0; 4334 const int aType = doc_getDocumentType(pThis); 4335 const bool isText = (aType == LOK_DOCTYPE_TEXT); 4336 const bool isCalc = (aType == LOK_DOCTYPE_SPREADSHEET); 4337 int nOrigEditMode = 0; 4338 bool bPaintTextEdit = true; 4339 int nViewId = nOrigViewId; 4340 int nLastNonEditorView = -1; 4341 int nViewMatchingMode = -1; 4342 SfxViewShell* pCurrentViewShell = SfxViewShell::Current(); 4343 4344 if (!isText) 4345 { 4346 // Check if just switching to another view is enough, that has 4347 // less side-effects. 4348 if (nPart != doc_getPart(pThis) || nMode != pDoc->getEditMode()) 4349 { 4350 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 4351 while (pViewShell) 4352 { 4353 bool bIsInEdit = pViewShell->GetDrawView() && 4354 pViewShell->GetDrawView()->GetTextEditOutliner(); 4355 4356 OString sCurrentViewRenderState = pDoc->getViewRenderState(pCurrentViewShell); 4357 OString sNewRenderState = pDoc->getViewRenderState(pViewShell); 4358 4359 if (sCurrentViewRenderState == sNewRenderState && !bIsInEdit) 4360 nLastNonEditorView = pViewShell->GetViewShellId().get(); 4361 4362 if (pViewShell->getPart() == nPart && 4363 pViewShell->getEditMode() == nMode && 4364 sCurrentViewRenderState == sNewRenderState && 4365 !bIsInEdit) 4366 { 4367 nViewId = pViewShell->GetViewShellId().get(); 4368 nViewMatchingMode = nViewId; 4369 nLastNonEditorView = nViewId; 4370 doc_setView(pThis, nViewId); 4371 break; 4372 } 4373 else if (pViewShell->getEditMode() == nMode && sCurrentViewRenderState == sNewRenderState && !bIsInEdit) 4374 { 4375 nViewMatchingMode = pViewShell->GetViewShellId().get(); 4376 } 4377 4378 pViewShell = SfxViewShell::GetNext(*pViewShell); 4379 } 4380 } 4381 4382 // if not found view with correct part 4383 // - at least avoid rendering active textbox, This is for Impress. 4384 // - prefer view with the same mode 4385 if (nViewMatchingMode >= 0 && nViewMatchingMode != nViewId) 4386 { 4387 nViewId = nViewMatchingMode; 4388 doc_setView(pThis, nViewId); 4389 } 4390 else if (!isCalc && nLastNonEditorView >= 0 && nLastNonEditorView != nViewId && 4391 pCurrentViewShell && pCurrentViewShell->GetDrawView() && 4392 pCurrentViewShell->GetDrawView()->GetTextEditOutliner()) 4393 { 4394 nViewId = nLastNonEditorView; 4395 doc_setView(pThis, nViewId); 4396 } 4397 4398 // Disable callbacks while we are painting - after setting the view 4399 if (nViewId != nOrigViewId && nViewId >= 0) 4400 { 4401 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId); 4402 if (handlerIt != pDocument->mpCallbackFlushHandlers.end()) 4403 handlerIt->second->disableCallbacks(); 4404 } 4405 4406 nOrigPart = doc_getPart(pThis); 4407 if (nPart != nOrigPart) 4408 { 4409 doc_setPartImpl(pThis, nPart, false); 4410 } 4411 4412 nOrigEditMode = pDoc->getEditMode(); 4413 if (nOrigEditMode != nMode) 4414 { 4415 SfxLokHelper::setEditMode(nMode, pDoc); 4416 } 4417 4418 bPaintTextEdit = (nPart == nOrigPart && nMode == nOrigEditMode); 4419 pDoc->setPaintTextEdit(bPaintTextEdit); 4420 } 4421 4422 doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight); 4423 4424 if (!isText) 4425 { 4426 pDoc->setPaintTextEdit(true); 4427 4428 if (nMode != nOrigEditMode) 4429 { 4430 SfxLokHelper::setEditMode(nOrigEditMode, pDoc); 4431 } 4432 4433 if (nPart != nOrigPart) 4434 { 4435 doc_setPartImpl(pThis, nOrigPart, false); 4436 } 4437 4438 if (nViewId != nOrigViewId) 4439 { 4440 if (nViewId >= 0) 4441 { 4442 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId); 4443 if (handlerIt != pDocument->mpCallbackFlushHandlers.end()) 4444 handlerIt->second->enableCallbacks(); 4445 } 4446 4447 doc_setView(pThis, nOrigViewId); 4448 } 4449 } 4450 } 4451 catch (const std::exception&) 4452 { 4453 // Nothing to do but restore the PartTilePainting flag. 4454 } 4455 4456 if (nOrigViewId >= 0) 4457 { 4458 const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId); 4459 if (handlerIt != pDocument->mpCallbackFlushHandlers.end()) 4460 handlerIt->second->enableCallbacks(); 4461 } 4462 } 4463 4464 static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/) 4465 { 4466 SetLastExceptionMsg(); 4467 #if ENABLE_CAIRO_RGBA || defined IOS 4468 return LOK_TILEMODE_RGBA; 4469 #else 4470 return LOK_TILEMODE_BGRA; 4471 #endif 4472 } 4473 4474 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis, 4475 long* pWidth, 4476 long* pHeight) 4477 { 4478 comphelper::ProfileZone aZone("doc_getDocumentSize"); 4479 4480 SolarMutexGuard aGuard; 4481 SetLastExceptionMsg(); 4482 4483 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4484 if (pDoc) 4485 { 4486 Size aDocumentSize = pDoc->getDocumentSize(); 4487 *pWidth = aDocumentSize.Width(); 4488 *pHeight = aDocumentSize.Height(); 4489 } 4490 else 4491 { 4492 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4493 } 4494 } 4495 4496 static void doc_getDataArea(LibreOfficeKitDocument* pThis, 4497 long nTab, 4498 long* pCol, 4499 long* pRow) 4500 { 4501 comphelper::ProfileZone aZone("doc_getDataArea"); 4502 4503 SolarMutexGuard aGuard; 4504 SetLastExceptionMsg(); 4505 4506 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4507 if (pDoc) 4508 { 4509 Size aDocumentSize = pDoc->getDataArea(nTab); 4510 *pCol = aDocumentSize.Width(); 4511 *pRow = aDocumentSize.Height(); 4512 } 4513 else 4514 { 4515 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4516 } 4517 } 4518 4519 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis, 4520 const char* pArguments) 4521 { 4522 comphelper::ProfileZone aZone("doc_initializeForRendering"); 4523 4524 SolarMutexGuard aGuard; 4525 SetLastExceptionMsg(); 4526 4527 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4528 if (pDoc) 4529 { 4530 doc_iniUnoCommands(); 4531 pDoc->initializeForTiledRendering( 4532 comphelper::containerToSequence(jsonToPropertyValuesVector(pArguments))); 4533 } 4534 } 4535 4536 static void doc_registerCallback(LibreOfficeKitDocument* pThis, 4537 LibreOfficeKitCallback pCallback, 4538 void* pData) 4539 { 4540 SolarMutexGuard aGuard; 4541 SetLastExceptionMsg(); 4542 4543 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 4544 4545 const int nView = SfxLokHelper::getView(); 4546 if (nView < 0) 4547 return; 4548 4549 const size_t nId = nView; 4550 if (pCallback != nullptr) 4551 { 4552 for (auto& pair : pDocument->mpCallbackFlushHandlers) 4553 { 4554 if (pair.first == nId) 4555 continue; 4556 4557 pair.second->addViewStates(nView); 4558 } 4559 } 4560 else 4561 { 4562 for (auto& pair : pDocument->mpCallbackFlushHandlers) 4563 { 4564 if (pair.first == nId) 4565 continue; 4566 4567 pair.second->removeViewStates(nView); 4568 } 4569 } 4570 4571 pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData); 4572 4573 if (pCallback != nullptr) 4574 { 4575 for (const auto& pair : pDocument->mpCallbackFlushHandlers) 4576 { 4577 if (pair.first == nId) 4578 continue; 4579 4580 pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first); 4581 } 4582 4583 if (SfxViewShell* pViewShell = SfxViewShell::Current()) 4584 { 4585 pDocument->mpCallbackFlushHandlers[nView]->setViewId(pViewShell->GetViewShellId().get()); 4586 pViewShell->setLibreOfficeKitViewCallback(pDocument->mpCallbackFlushHandlers[nView].get()); 4587 } 4588 4589 if (!pDocument->maFontsMissing.empty()) 4590 { 4591 OString sPayload = "{ \"fontsmissing\": [ "_ostr; 4592 bool bFirst = true; 4593 for (const auto &f : pDocument->maFontsMissing) 4594 { 4595 if (bFirst) 4596 bFirst = false; 4597 else 4598 sPayload += ", "; 4599 sPayload += "\"" + f.toUtf8() + "\""; 4600 } 4601 sPayload += " ] }"; 4602 pCallback(LOK_CALLBACK_FONTS_MISSING, sPayload.getStr(), pData); 4603 pDocument->maFontsMissing.clear(); 4604 } 4605 } 4606 else 4607 { 4608 if (SfxViewShell* pViewShell = SfxViewShell::Current()) 4609 { 4610 pViewShell->setLibreOfficeKitViewCallback(nullptr); 4611 pDocument->mpCallbackFlushHandlers[nView]->setViewId(-1); 4612 } 4613 } 4614 } 4615 4616 /// Returns the JSON representation of all the comments in the document 4617 static char* getPostIts(LibreOfficeKitDocument* pThis) 4618 { 4619 SetLastExceptionMsg(); 4620 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4621 if (!pDoc) 4622 { 4623 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4624 return nullptr; 4625 } 4626 tools::JsonWriter aJsonWriter; 4627 pDoc->getPostIts(aJsonWriter); 4628 return convertOString(aJsonWriter.finishAndGetAsOString()); 4629 } 4630 4631 /// Returns the JSON representation of the positions of all the comments in the document 4632 static char* getPostItsPos(LibreOfficeKitDocument* pThis) 4633 { 4634 SetLastExceptionMsg(); 4635 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4636 if (!pDoc) 4637 { 4638 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4639 return nullptr; 4640 } 4641 tools::JsonWriter aJsonWriter; 4642 pDoc->getPostItsPos(aJsonWriter); 4643 return convertOString(aJsonWriter.finishAndGetAsOString()); 4644 } 4645 4646 static char* getRulerState(LibreOfficeKitDocument* pThis) 4647 { 4648 SetLastExceptionMsg(); 4649 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4650 if (!pDoc) 4651 { 4652 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4653 return nullptr; 4654 } 4655 tools::JsonWriter aJsonWriter; 4656 pDoc->getRulerState(aJsonWriter); 4657 return convertOString(aJsonWriter.finishAndGetAsOString()); 4658 } 4659 4660 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode) 4661 { 4662 comphelper::ProfileZone aZone("doc_postKeyEvent"); 4663 4664 SolarMutexGuard aGuard; 4665 SetLastExceptionMsg(); 4666 4667 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4668 if (!pDoc) 4669 { 4670 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4671 return; 4672 } 4673 4674 try 4675 { 4676 pDoc->postKeyEvent(nType, nCharCode, nKeyCode); 4677 } 4678 catch (const uno::Exception& exception) 4679 { 4680 SetLastExceptionMsg(exception.Message); 4681 SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message); 4682 } 4683 } 4684 4685 static void doc_setBlockedCommandList(LibreOfficeKitDocument* /*pThis*/, int nViewId, const char* blockedCommandList) 4686 { 4687 SolarMutexGuard aGuard; 4688 SfxLokHelper::setBlockedCommandList(nViewId, blockedCommandList); 4689 } 4690 4691 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText) 4692 { 4693 comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent"); 4694 4695 SolarMutexGuard aGuard; 4696 VclPtr<vcl::Window> pWindow; 4697 if (nWindowId == 0) 4698 { 4699 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4700 if (!pDoc) 4701 { 4702 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4703 return; 4704 } 4705 pWindow = pDoc->getDocWindow(); 4706 } 4707 else 4708 { 4709 pWindow = vcl::Window::FindLOKWindow(nWindowId); 4710 } 4711 4712 if (!pWindow) 4713 { 4714 SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId)); 4715 return; 4716 } 4717 4718 SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(std::string_view(pText, strlen(pText)))); 4719 } 4720 4721 static char* doc_hyperlinkInfoAtPosition(LibreOfficeKitDocument* pThis, int x, int y) 4722 { 4723 SolarMutexGuard aGuard; 4724 4725 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4726 if (!pDoc) 4727 { 4728 SetLastExceptionMsg("Document doesn't support tiled rendering"); 4729 return nullptr; 4730 } 4731 4732 return convertOUString(pDoc->hyperlinkInfoAtPosition(x, y)); 4733 } 4734 4735 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter) 4736 { 4737 SolarMutexGuard aGuard; 4738 VclPtr<vcl::Window> pWindow; 4739 if (nLOKWindowId == 0) 4740 { 4741 ITiledRenderable* pDoc = getTiledRenderable(pThis); 4742 if (!pDoc) 4743 { 4744 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 4745 return; 4746 } 4747 pWindow = pDoc->getDocWindow(); 4748 } 4749 else 4750 { 4751 pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 4752 } 4753 4754 if (!pWindow) 4755 { 4756 SetLastExceptionMsg("No window found for window id: " + OUString::number(nLOKWindowId)); 4757 return; 4758 } 4759 4760 // Annoyingly - backspace and delete are handled in the apps via an accelerator 4761 // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same 4762 // order we do this synchronously here, unless we're in a dialog. 4763 if (nCharBefore > 0) 4764 { 4765 // backspace 4766 if (nLOKWindowId == 0) 4767 { 4768 KeyEvent aEvt(8, KEY_BACKSPACE); 4769 for (int i = 0; i < nCharBefore; ++i) 4770 pWindow->KeyInput(aEvt); 4771 } 4772 else 4773 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, KEY_BACKSPACE, nCharBefore - 1); 4774 } 4775 4776 if (nCharAfter > 0) 4777 { 4778 // delete (forward) 4779 if (nLOKWindowId == 0) 4780 { 4781 KeyEvent aEvt(46, KEY_DELETE); 4782 for (int i = 0; i < nCharAfter; ++i) 4783 pWindow->KeyInput(aEvt); 4784 } 4785 else 4786 SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, KEY_DELETE, nCharAfter - 1); 4787 } 4788 } 4789 4790 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode) 4791 { 4792 comphelper::ProfileZone aZone("doc_postWindowKeyEvent"); 4793 4794 SolarMutexGuard aGuard; 4795 SetLastExceptionMsg(); 4796 4797 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 4798 if (!pWindow) 4799 { 4800 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 4801 return; 4802 } 4803 4804 KeyEvent aEvent(nCharCode, nKeyCode, 0); 4805 4806 switch (nType) 4807 { 4808 case LOK_KEYEVENT_KEYINPUT: 4809 Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent); 4810 break; 4811 case LOK_KEYEVENT_KEYUP: 4812 Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent); 4813 break; 4814 default: 4815 assert(false); 4816 break; 4817 } 4818 } 4819 4820 // To be an exportable selection, there must be something selected and that 4821 // selection can't be "ScCellObj" which doesn't can't provide a svg. 4822 // 4823 // Typically a problem arises when double clicking a shape in calc. The 1st 4824 // click selects the shape, triggering generation of a preview, but the second 4825 // shape enters into edit mode before doc_renderShapeSelection has a chance to 4826 // fire, at which point the shape is no longer selected. Rather than generate 4827 // an error just return a 0 length result if there is no shape selected, so we 4828 // continue to generate an error if a shape is selected, but could not provide 4829 // an svg. 4830 static bool doc_hasShapeSelection(const css::uno::Reference<css::lang::XComponent>& rComponent) 4831 { 4832 uno::Reference<frame::XModel> xModel(rComponent, uno::UNO_QUERY); 4833 if (!xModel.is()) 4834 return false; 4835 4836 uno::Reference<frame::XController> xController(xModel->getCurrentController()); 4837 if (!xController.is()) 4838 return false; 4839 4840 uno::Reference<view::XSelectionSupplier> xSelectionSupplier(xController, uno::UNO_QUERY); 4841 if (!xSelectionSupplier.is()) 4842 return false; 4843 4844 Any selection = xSelectionSupplier->getSelection(); 4845 uno::Reference<lang::XServiceInfo> xSelection; 4846 selection >>= xSelection; 4847 4848 return xSelection && xSelection->getImplementationName() != "ScCellObj"; 4849 } 4850 4851 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput) 4852 { 4853 comphelper::ProfileZone aZone("doc_renderShapeSelection"); 4854 4855 SolarMutexGuard aGuard; 4856 SetLastExceptionMsg(); 4857 4858 LokChartHelper aChartHelper(SfxViewShell::Current()); 4859 4860 if (aChartHelper.GetWindow()) 4861 return 0; 4862 4863 try 4864 { 4865 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 4866 4867 if (!doc_hasShapeSelection(pDocument->mxComponent)) 4868 return 0; 4869 4870 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW); 4871 4872 SvMemoryStream aOutStream; 4873 uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream); 4874 4875 utl::MediaDescriptor aMediaDescriptor; 4876 switch (doc_getDocumentType(pThis)) 4877 { 4878 case LOK_DOCTYPE_PRESENTATION: 4879 aMediaDescriptor[u"FilterName"_ustr] <<= u"impress_svg_Export"_ustr; 4880 break; 4881 case LOK_DOCTYPE_DRAWING: 4882 aMediaDescriptor[u"FilterName"_ustr] <<= u"draw_svg_Export"_ustr; 4883 break; 4884 case LOK_DOCTYPE_TEXT: 4885 aMediaDescriptor[u"FilterName"_ustr] <<= u"writer_svg_Export"_ustr; 4886 break; 4887 case LOK_DOCTYPE_SPREADSHEET: 4888 aMediaDescriptor[u"FilterName"_ustr] <<= u"calc_svg_Export"_ustr; 4889 break; 4890 default: 4891 SAL_WARN("lok", "Failed to render shape selection: Document type is not supported"); 4892 } 4893 aMediaDescriptor[u"SelectionOnly"_ustr] <<= true; 4894 aMediaDescriptor[u"OutputStream"_ustr] <<= xOut; 4895 aMediaDescriptor[u"IsPreview"_ustr] <<= true; // will down-scale graphics 4896 4897 xStorable->storeToURL(u"private:stream"_ustr, aMediaDescriptor.getAsConstPropertyValueList()); 4898 4899 if (pOutput) 4900 { 4901 const size_t nOutputSize = aOutStream.GetEndOfData(); 4902 *pOutput = static_cast<char*>(malloc(nOutputSize)); 4903 if (*pOutput) 4904 { 4905 std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize); 4906 return nOutputSize; 4907 } 4908 } 4909 } 4910 catch (const uno::Exception& exception) 4911 { 4912 css::uno::Any exAny( cppu::getCaughtException() ); 4913 SetLastExceptionMsg(exception.Message); 4914 SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny)); 4915 } 4916 4917 return 0; 4918 } 4919 4920 namespace { 4921 4922 /** Class to react on finishing of a dispatched command. 4923 4924 This will call a LOK_COMMAND_FINISHED callback when postUnoCommand was 4925 called with the parameter requesting the notification. 4926 4927 @see LibreOfficeKitCallbackType::LOK_CALLBACK_UNO_COMMAND_RESULT. 4928 */ 4929 class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener> 4930 { 4931 const OString maCommand; ///< Command for which this is the result. 4932 const std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call. 4933 const std::chrono::steady_clock::time_point mSaveTime; //< The time we started saving. 4934 const bool mbWasModified; //< Whether or not the document was modified before saving. 4935 4936 public: 4937 DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> pCallback) 4938 : maCommand(pCommand) 4939 , mpCallback(std::move(pCallback)) 4940 , mSaveTime(std::chrono::steady_clock::now()) 4941 , mbWasModified(SfxObjectShell::Current()->IsModified()) 4942 { 4943 assert(mpCallback); 4944 } 4945 4946 virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override 4947 { 4948 tools::JsonWriter aJson; 4949 aJson.put("commandName", maCommand); 4950 4951 if (rEvent.State != frame::DispatchResultState::DONTKNOW) 4952 { 4953 bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS); 4954 aJson.put("success", bSuccess); 4955 } 4956 4957 unoAnyToJson(aJson, "result", rEvent.Result); 4958 aJson.put("wasModified", mbWasModified); 4959 aJson.put("startUnixTimeMics", 4960 std::chrono::time_point_cast<std::chrono::microseconds>(mSaveTime) 4961 .time_since_epoch() 4962 .count()); 4963 aJson.put("saveDurationMics", std::chrono::duration_cast<std::chrono::microseconds>( 4964 std::chrono::steady_clock::now() - mSaveTime) 4965 .count()); 4966 mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString()); 4967 } 4968 4969 virtual void SAL_CALL disposing(const css::lang::EventObject&) override {} 4970 }; 4971 4972 } // anonymous namespace 4973 4974 4975 static void lcl_sendDialogEvent(unsigned long long int nWindowId, const char* pArguments) 4976 { 4977 SolarMutexGuard aGuard; 4978 4979 StringMap aMap(jsdialog::jsonToStringMap(pArguments)); 4980 4981 if (aMap.find(u"id"_ustr) == aMap.end()) 4982 return; 4983 4984 sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current()); 4985 4986 try 4987 { 4988 OUString sControlId = aMap[u"id"_ustr]; 4989 OUString sWindowId = OUString::number(nWindowId); 4990 OUString sCurrentShellId = OUString::number(nCurrentShellId); 4991 4992 // special values for window id 4993 if (nWindowId == static_cast<unsigned long long int>(-1)) 4994 sWindowId = sCurrentShellId + "sidebar"; 4995 if (nWindowId == static_cast<unsigned long long int>(-2)) 4996 sWindowId = sCurrentShellId + "notebookbar"; 4997 if (nWindowId == static_cast<unsigned long long int>(-3)) 4998 sWindowId = sCurrentShellId + "formulabar"; 4999 5000 // dialogs send own id but notebookbar and sidebar controls are remembered by SfxViewShell id 5001 if (jsdialog::ExecuteAction(sWindowId, sControlId, aMap)) 5002 return; 5003 5004 if (jsdialog::ExecuteAction(sCurrentShellId + "sidebar", sControlId, aMap)) 5005 return; 5006 if (jsdialog::ExecuteAction(sCurrentShellId + "notebookbar", sControlId, aMap)) 5007 return; 5008 if (jsdialog::ExecuteAction(sCurrentShellId + "formulabar", sControlId, aMap)) 5009 return; 5010 // this is needed for dialogs shown before document is loaded: MacroWarning dialog, etc... 5011 // these dialogs are created with WindowId "0" 5012 if (!SfxViewShell::Current() && jsdialog::ExecuteAction(u"0"_ustr, sControlId, aMap)) 5013 return; 5014 5015 // force resend - used in mobile-wizard 5016 jsdialog::SendFullUpdate(sCurrentShellId + "sidebar", u"Panel"_ustr); 5017 5018 } catch(...) {} 5019 } 5020 5021 5022 static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned long long int nWindowId, const char* pArguments) 5023 { 5024 lcl_sendDialogEvent(nWindowId, pArguments); 5025 } 5026 5027 static void lo_sendDialogEvent(LibreOfficeKit* /*pThis*/, unsigned long long int nWindowId, const char* pArguments) 5028 { 5029 lcl_sendDialogEvent(nWindowId, pArguments); 5030 } 5031 5032 static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const char* pValue) 5033 { 5034 static char* pCurrentSalLogOverride = nullptr; 5035 5036 if (strcmp(pOption, "traceeventrecording") == 0) 5037 { 5038 if (strcmp(pValue, "start") == 0) 5039 { 5040 comphelper::TraceEvent::setBufferSizeAndCallback(100, TraceEventDumper::flushRecordings); 5041 comphelper::TraceEvent::startRecording(); 5042 if (traceEventDumper == nullptr) 5043 traceEventDumper = new TraceEventDumper(); 5044 } 5045 else if (strcmp(pValue, "stop") == 0) 5046 comphelper::TraceEvent::stopRecording(); 5047 } 5048 else if (strcmp(pOption, "sallogoverride") == 0) 5049 { 5050 if (pCurrentSalLogOverride != nullptr) 5051 free(pCurrentSalLogOverride); 5052 if (pValue == nullptr) 5053 pCurrentSalLogOverride = nullptr; 5054 else 5055 pCurrentSalLogOverride = strdup(pValue); 5056 5057 if (pCurrentSalLogOverride == nullptr || pCurrentSalLogOverride[0] == '\0') 5058 sal_detail_set_log_selector(nullptr); 5059 else 5060 sal_detail_set_log_selector(pCurrentSalLogOverride); 5061 } 5062 #ifdef LINUX 5063 else if (strcmp(pOption, "addfont") == 0) 5064 { 5065 if (memcmp(pValue, "file://", 7) == 0) 5066 pValue += 7; 5067 5068 int fd = open(pValue, O_RDONLY); 5069 if (fd == -1) 5070 { 5071 std::cerr << "Could not open font file '" << pValue << "': " << strerror(errno) << std::endl; 5072 return; 5073 } 5074 5075 OUString sMagicFileName = "file:///:FD:/" + OUString::number(fd); 5076 5077 OutputDevice *pDevice = Application::GetDefaultDevice(); 5078 OutputDevice::ImplClearAllFontData(false); 5079 pDevice->AddTempDevFont(sMagicFileName, ""); 5080 OutputDevice::ImplRefreshAllFontData(false); 5081 } 5082 #endif 5083 } 5084 5085 static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, char** pState) 5086 { 5087 if (!pState) 5088 return; 5089 5090 // NB. no SolarMutexGuard since this may be caused in some extremis / deadlock 5091 SetLastExceptionMsg(); 5092 5093 *pState = nullptr; 5094 OStringBuffer aState(4096*256); 5095 5096 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 5097 5098 pLib->dumpState(aState); 5099 5100 *pState = convertOString(aState.makeStringAndClear()); 5101 } 5102 5103 void LibLibreOffice_Impl::dumpState(rtl::OStringBuffer &rState) 5104 { 5105 rState.append("LibreOfficeKit state:" 5106 "\n\tLastExceptionMsg:\t"); 5107 rState.append(rtl::OUStringToOString(maLastExceptionMsg, RTL_TEXTENCODING_UTF8)); 5108 rState.append("\n\tUnipoll:\t"); 5109 rState.append(vcl::lok::isUnipoll() ? "yes" : "no: events on thread"); 5110 rState.append("\n\tOptionalFeatures:\t0x"); 5111 rState.append(static_cast<sal_Int64>(mOptionalFeatures), 16); 5112 rState.append("\n\tCallbackData:\t0x"); 5113 rState.append(reinterpret_cast<sal_Int64>(mpCallback), 16); 5114 // TODO: dump mInteractionMap 5115 SfxLokHelper::dumpState(rState); 5116 vcl::lok::dumpState(rState); 5117 } 5118 5119 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished) 5120 { 5121 comphelper::ProfileZone aZone("doc_postUnoCommand"); 5122 5123 SolarMutexGuard aGuard; 5124 SetLastExceptionMsg(); 5125 5126 SfxObjectShell* pDocSh = SfxObjectShell::Current(); 5127 OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8); 5128 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 5129 5130 std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments)); 5131 5132 if (!vcl::lok::isUnipoll()) 5133 { 5134 beans::PropertyValue aSynchronMode; 5135 aSynchronMode.Name = u"SynchronMode"_ustr; 5136 aSynchronMode.Value <<= false; 5137 aPropertyValuesVector.push_back(aSynchronMode); 5138 } 5139 5140 int nView = SfxLokHelper::getView(); 5141 if (nView < 0) 5142 return; 5143 5144 if (gImpl && aCommand == ".uno:ToggleOrientation") 5145 { 5146 ExecuteOrientationChange(); 5147 return; 5148 } 5149 5150 // handle potential interaction 5151 if (gImpl && aCommand == ".uno:Save") 5152 { 5153 // Check if saving a PDF file 5154 OUString aMimeType = lcl_getCurrentDocumentMimeType(pDocument); 5155 if (pDocSh && pDocSh->IsModified() && aMimeType == "application/pdf") 5156 { 5157 // If we have a PDF file (for saving annotations for example), we need 5158 // to run save-as to the same file as the opened document. Plain save 5159 // doesn't work as the PDF is not a "native" format. 5160 uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW); 5161 OUString aURL = xStorable->getLocation(); 5162 OString aURLUtf8 = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8); 5163 bool bResult = doc_saveAs(pThis, aURLUtf8.getStr(), "pdf", nullptr); 5164 5165 // Send the result of save 5166 tools::JsonWriter aJson; 5167 aJson.put("commandName", pCommand); 5168 aJson.put("success", bResult); 5169 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString()); 5170 return; 5171 } 5172 5173 5174 rtl::Reference<LOKInteractionHandler> const pInteraction( 5175 new LOKInteractionHandler("save"_ostr, gImpl, pDocument)); 5176 uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction); 5177 5178 beans::PropertyValue aValue; 5179 aValue.Name = u"InteractionHandler"_ustr; 5180 aValue.Value <<= xInteraction; 5181 aPropertyValuesVector.push_back(aValue); 5182 5183 bool bDontSaveIfUnmodified = false; 5184 std::erase_if(aPropertyValuesVector, 5185 [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){ 5186 if (aItem.Name == "DontSaveIfUnmodified") 5187 { 5188 bDontSaveIfUnmodified = aItem.Value.get<bool>(); 5189 return true; 5190 } 5191 return false; 5192 }); 5193 5194 // skip saving and tell the result via UNO_COMMAND_RESULT 5195 if (bDontSaveIfUnmodified && (!pDocSh || !pDocSh->IsModified())) 5196 { 5197 tools::JsonWriter aJson; 5198 aJson.put("commandName", pCommand); 5199 aJson.put("success", false); 5200 // Add the reason for not saving 5201 { 5202 auto resultNode = aJson.startNode("result"); 5203 aJson.put("type", "string"); 5204 aJson.put("value", "unmodified"); 5205 } 5206 pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.finishAndGetAsOString()); 5207 return; 5208 } 5209 } 5210 else if (gImpl && aCommand == ".uno:TransformDialog") 5211 { 5212 bool bNeedConversion = false; 5213 SfxViewShell* pViewShell = SfxViewShell::Current(); 5214 LokChartHelper aChartHelper(pViewShell); 5215 5216 if (aChartHelper.GetWindow() ) 5217 { 5218 bNeedConversion = true; 5219 } 5220 else if (const SdrView* pView = pViewShell->GetDrawView()) 5221 { 5222 if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice()) 5223 { 5224 bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM); 5225 } 5226 } 5227 5228 if (bNeedConversion) 5229 { 5230 sal_Int32 value; 5231 for (beans::PropertyValue& rPropValue: aPropertyValuesVector) 5232 { 5233 if (rPropValue.Name == "TransformPosX" 5234 || rPropValue.Name == "TransformPosY" 5235 || rPropValue.Name == "TransformWidth" 5236 || rPropValue.Name == "TransformHeight" 5237 || rPropValue.Name == "TransformRotationX" 5238 || rPropValue.Name == "TransformRotationY") 5239 { 5240 rPropValue.Value >>= value; 5241 value = o3tl::convert(value, o3tl::Length::twip, o3tl::Length::mm100); 5242 rPropValue.Value <<= value; 5243 } 5244 } 5245 } 5246 5247 if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0) 5248 { 5249 if (aPropertyValuesVector[0].Name != "Action") 5250 { 5251 tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox(); 5252 5253 int nLeft = o3tl::convert(aChartBB.Left(), o3tl::Length::twip, o3tl::Length::mm100); 5254 int nTop = o3tl::convert(aChartBB.Top(), o3tl::Length::twip, o3tl::Length::mm100); 5255 5256 for (beans::PropertyValue& rPropValue: aPropertyValuesVector) 5257 { 5258 if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX") 5259 { 5260 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value); 5261 rPropValue.Value <<= value - nLeft; 5262 } 5263 else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY") 5264 { 5265 auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value); 5266 rPropValue.Value <<= value - nTop; 5267 } 5268 } 5269 } 5270 util::URL aCommandURL; 5271 aCommandURL.Path = u"LOKTransform"_ustr; 5272 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher(); 5273 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector)); 5274 return; 5275 } 5276 } 5277 else if (gImpl && aCommand == ".uno:LOKSidebarWriterPage") 5278 { 5279 setupSidebar(u"WriterPageDeck"); 5280 return; 5281 } 5282 else if (gImpl && aCommand == ".uno:SidebarShow") 5283 { 5284 setupSidebar(); 5285 return; 5286 } 5287 else if (gImpl && aCommand == ".uno:SidebarHide") 5288 { 5289 hideSidebar(); 5290 return; 5291 } 5292 5293 bool bResult = false; 5294 LokChartHelper aChartHelper(SfxViewShell::Current()); 5295 5296 if (aChartHelper.GetWindow() && aCommand != ".uno:Save" ) 5297 { 5298 util::URL aCommandURL; 5299 aCommandURL.Path = aCommand.copy(5); 5300 css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher(); 5301 aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector)); 5302 return; 5303 } 5304 if (LokStarMathHelper aMathHelper(SfxViewShell::Current()); 5305 aMathHelper.GetGraphicWindow() && aCommand != ".uno:Save") 5306 { 5307 aMathHelper.Dispatch(aCommand, comphelper::containerToSequence(aPropertyValuesVector)); 5308 return; 5309 } 5310 if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView)) 5311 { 5312 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector), 5313 new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView])); 5314 } 5315 else 5316 bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector)); 5317 5318 if (!bResult) 5319 { 5320 SetLastExceptionMsg("Failed to dispatch " + aCommand); 5321 } 5322 } 5323 5324 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier) 5325 { 5326 comphelper::ProfileZone aZone("doc_postMouseEvent"); 5327 5328 SolarMutexGuard aGuard; 5329 SetLastExceptionMsg(); 5330 5331 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5332 if (!pDoc) 5333 { 5334 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5335 return; 5336 } 5337 try 5338 { 5339 pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier); 5340 } 5341 catch (const uno::Exception& exception) 5342 { 5343 SetLastExceptionMsg(exception.Message); 5344 SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message); 5345 } 5346 } 5347 5348 static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier) 5349 { 5350 comphelper::ProfileZone aZone("doc_postWindowMouseEvent"); 5351 5352 SolarMutexGuard aGuard; 5353 SetLastExceptionMsg(); 5354 5355 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 5356 if (!pWindow) 5357 { 5358 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 5359 return; 5360 } 5361 5362 const Point aPos(nX, nY); 5363 5364 MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier); 5365 5366 vcl::EnableDialogInput(pWindow); 5367 5368 switch (nType) 5369 { 5370 case LOK_MOUSEEVENT_MOUSEBUTTONDOWN: 5371 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent); 5372 break; 5373 case LOK_MOUSEEVENT_MOUSEBUTTONUP: 5374 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent); 5375 break; 5376 case LOK_MOUSEEVENT_MOUSEMOVE: 5377 Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent); 5378 break; 5379 default: 5380 assert(false); 5381 break; 5382 } 5383 } 5384 5385 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset) 5386 { 5387 comphelper::ProfileZone aZone("doc_postWindowGestureEvent"); 5388 5389 SolarMutexGuard aGuard; 5390 SetLastExceptionMsg(); 5391 5392 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 5393 if (!pWindow) 5394 { 5395 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 5396 return; 5397 } 5398 5399 OString aType(pType); 5400 GestureEventPanType eEventType = GestureEventPanType::Update; 5401 5402 if (aType == "panBegin") 5403 eEventType = GestureEventPanType::Begin; 5404 else if (aType == "panEnd") 5405 eEventType = GestureEventPanType::End; 5406 5407 GestureEventPan aEvent { 5408 sal_Int32(nX), 5409 sal_Int32(nY), 5410 eEventType, 5411 sal_Int32(nOffset), 5412 PanningOrientation::Vertical, 5413 }; 5414 5415 vcl::EnableDialogInput(pWindow); 5416 5417 Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent); 5418 } 5419 5420 static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY) 5421 { 5422 comphelper::ProfileZone aZone("doc_setTextSelection"); 5423 5424 SolarMutexGuard aGuard; 5425 SetLastExceptionMsg(); 5426 5427 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5428 if (!pDoc) 5429 { 5430 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5431 return; 5432 } 5433 5434 pDoc->setTextSelection(nType, nX, nY); 5435 } 5436 5437 static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY) 5438 { 5439 comphelper::ProfileZone aZone("doc_setWindowTextSelection"); 5440 5441 SolarMutexGuard aGuard; 5442 SetLastExceptionMsg(); 5443 5444 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 5445 if (!pWindow) 5446 { 5447 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 5448 return; 5449 } 5450 5451 5452 Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel()); 5453 Point aCursorPos(nX, nY); 5454 aCursorPos.Move(aOffset); 5455 sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT; 5456 5457 MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier); 5458 Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent); 5459 Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent); 5460 } 5461 5462 static bool getFromTransferable( 5463 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, 5464 const OString &aInMimeType, OString &aRet); 5465 5466 static bool encodeImageAsHTML( 5467 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, 5468 const OString &aMimeType, OString &aRet) 5469 { 5470 if (!getFromTransferable(xTransferable, aMimeType, aRet)) 5471 return false; 5472 5473 // Encode in base64. 5474 auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()), 5475 aRet.getLength()); 5476 OStringBuffer aBase64Data; 5477 comphelper::Base64::encode(aBase64Data, aSeq); 5478 5479 // Embed in HTML. 5480 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" 5481 "<html><head>" 5482 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " 5483 "name=\"generator\" content=\"" 5484 + getGenerator().toUtf8() 5485 + "\"/>" 5486 "</head><body><img src=\"data:" + aMimeType + ";base64," 5487 + aBase64Data + "\"/></body></html>"; 5488 5489 return true; 5490 } 5491 5492 static bool encodeTextAsHTML( 5493 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, 5494 const OString &aMimeType, OString &aRet) 5495 { 5496 if (!getFromTransferable(xTransferable, aMimeType, aRet)) 5497 return false; 5498 5499 // Embed in HTML - FIXME: needs some escaping. 5500 aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" 5501 "<html><head>" 5502 "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta " 5503 "name=\"generator\" content=\"" 5504 + getGenerator().toUtf8() 5505 + "\"/></head><body><pre>" + aRet + "</pre></body></html>"; 5506 5507 return true; 5508 } 5509 5510 static bool getFromTransferable( 5511 const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable, 5512 const OString &aInMimeType, OString &aRet) 5513 { 5514 OString aMimeType(aInMimeType); 5515 5516 // Take care of UTF-8 text here. 5517 bool bConvert = false; 5518 sal_Int32 nIndex = 0; 5519 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "text/plain") 5520 { 5521 if (o3tl::getToken(aMimeType, 0, ';', nIndex) == "charset=utf-8") 5522 { 5523 aMimeType = "text/plain;charset=utf-16"_ostr; 5524 bConvert = true; 5525 } 5526 } 5527 5528 datatransfer::DataFlavor aFlavor; 5529 aFlavor.MimeType = OUString::fromUtf8(aMimeType); 5530 if (aMimeType == "text/plain;charset=utf-16") 5531 aFlavor.DataType = cppu::UnoType<OUString>::get(); 5532 else 5533 aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get(); 5534 5535 if (!xTransferable->isDataFlavorSupported(aFlavor)) 5536 { 5537 // Try harder for HTML it is our copy/paste meta-file format 5538 if (aInMimeType == "text/html") 5539 { 5540 // Desperate measures - convert text to HTML instead. 5541 if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8"_ostr, aRet)) 5542 return true; 5543 // If html is not supported, might be a graphic-selection, 5544 if (encodeImageAsHTML(xTransferable, "image/png"_ostr, aRet)) 5545 return true; 5546 } 5547 5548 SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported"); 5549 return false; 5550 } 5551 5552 uno::Any aAny; 5553 try 5554 { 5555 aAny = xTransferable->getTransferData(aFlavor); 5556 } 5557 catch (const css::datatransfer::UnsupportedFlavorException& e) 5558 { 5559 SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message); 5560 return false; 5561 } 5562 catch (const css::uno::Exception& e) 5563 { 5564 SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message); 5565 return false; 5566 } 5567 5568 if (aFlavor.DataType == cppu::UnoType<OUString>::get()) 5569 { 5570 OUString aString; 5571 aAny >>= aString; 5572 if (bConvert) 5573 aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8); 5574 else 5575 aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode)); 5576 } 5577 else 5578 { 5579 uno::Sequence<sal_Int8> aSequence; 5580 aAny >>= aSequence; 5581 aRet = OString(reinterpret_cast<const char*>(aSequence.getConstArray()), aSequence.getLength()); 5582 } 5583 5584 return true; 5585 } 5586 5587 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType) 5588 { 5589 comphelper::ProfileZone aZone("doc_getTextSelection"); 5590 5591 SolarMutexGuard aGuard; 5592 SetLastExceptionMsg(); 5593 5594 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5595 if (!pDoc) 5596 { 5597 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5598 return nullptr; 5599 } 5600 5601 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection(); 5602 if (!xTransferable) 5603 { 5604 SetLastExceptionMsg(u"No selection available"_ustr); 5605 return nullptr; 5606 } 5607 5608 OString aType 5609 = pMimeType && pMimeType[0] != '\0' ? OString(pMimeType) : "text/plain;charset=utf-8"_ostr; 5610 5611 OString aRet; 5612 bool bSuccess = getFromTransferable(xTransferable, aType, aRet); 5613 if (!bSuccess) 5614 return nullptr; 5615 5616 if (pUsedMimeType) // legacy 5617 { 5618 if (pMimeType) 5619 *pUsedMimeType = strdup(pMimeType); 5620 else 5621 *pUsedMimeType = nullptr; 5622 } 5623 5624 return convertOString(aRet); 5625 } 5626 5627 static int doc_getSelectionType(LibreOfficeKitDocument* pThis) 5628 { 5629 comphelper::ProfileZone aZone("doc_getSelectionType"); 5630 5631 SolarMutexGuard aGuard; 5632 SetLastExceptionMsg(); 5633 5634 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5635 if (!pDoc) 5636 { 5637 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5638 return LOK_SELTYPE_NONE; 5639 } 5640 5641 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection(); 5642 if (!xTransferable) 5643 { 5644 SetLastExceptionMsg(u"No selection available"_ustr); 5645 return LOK_SELTYPE_NONE; 5646 } 5647 5648 css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(xTransferable, css::uno::UNO_QUERY); 5649 if (xTransferable2.is() && xTransferable2->isComplex()) 5650 return LOK_SELTYPE_COMPLEX; 5651 5652 OString aRet; 5653 bool bSuccess = getFromTransferable(xTransferable, "text/plain;charset=utf-8"_ostr, aRet); 5654 if (!bSuccess) 5655 return LOK_SELTYPE_NONE; 5656 5657 if (aRet.getLength() > 10000) 5658 return LOK_SELTYPE_COMPLEX; 5659 5660 return !aRet.isEmpty() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE; 5661 } 5662 5663 static int doc_getSelectionTypeAndText(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pText, char** pUsedMimeType) 5664 { 5665 // The purpose of this function is to avoid double call to pDoc->getSelection(), 5666 // which may be expensive. 5667 comphelper::ProfileZone aZone("doc_getSelectionTypeAndText"); 5668 5669 SolarMutexGuard aGuard; 5670 SetLastExceptionMsg(); 5671 5672 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5673 if (!pDoc) 5674 { 5675 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5676 return LOK_SELTYPE_NONE; 5677 } 5678 5679 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection(); 5680 if (!xTransferable) 5681 { 5682 SetLastExceptionMsg(u"No selection available"_ustr); 5683 return LOK_SELTYPE_NONE; 5684 } 5685 5686 css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(xTransferable, css::uno::UNO_QUERY); 5687 if (xTransferable2.is() && xTransferable2->isComplex()) 5688 return LOK_SELTYPE_COMPLEX; 5689 5690 OString aType 5691 = pMimeType && pMimeType[0] != '\0' ? OString(pMimeType) : "text/plain;charset=utf-8"_ostr; 5692 5693 OString aRet; 5694 bool bSuccess = getFromTransferable(xTransferable, aType, aRet); 5695 if (!bSuccess) 5696 return LOK_SELTYPE_NONE; 5697 5698 if (aRet.getLength() > 10000) 5699 return LOK_SELTYPE_COMPLEX; 5700 5701 if (aRet.isEmpty()) 5702 return LOK_SELTYPE_NONE; 5703 5704 if (pText) 5705 *pText = convertOString(aRet); 5706 5707 if (pUsedMimeType) // legacy 5708 { 5709 if (pMimeType) 5710 *pUsedMimeType = strdup(pMimeType); 5711 else 5712 *pUsedMimeType = nullptr; 5713 } 5714 5715 return LOK_SELTYPE_TEXT; 5716 } 5717 5718 static int doc_getClipboard(LibreOfficeKitDocument* pThis, 5719 const char **pMimeTypes, 5720 size_t *pOutCount, 5721 char ***pOutMimeTypes, 5722 size_t **pOutSizes, 5723 char ***pOutStreams) 5724 { 5725 #ifdef IOS 5726 (void) pThis; 5727 (void) pMimeTypes; 5728 (void) pOutCount; 5729 (void) pOutMimeTypes; 5730 (void) pOutSizes; 5731 (void) pOutStreams; 5732 5733 assert(!"doc_getClipboard should not be called on iOS"); 5734 5735 return 0; 5736 #else 5737 comphelper::ProfileZone aZone("doc_getClipboard"); 5738 5739 SolarMutexGuard aGuard; 5740 SetLastExceptionMsg(); 5741 5742 assert (pOutCount); 5743 assert (pOutMimeTypes); 5744 assert (pOutSizes); 5745 assert (pOutStreams); 5746 5747 *pOutCount = 0; 5748 *pOutMimeTypes = nullptr; 5749 *pOutSizes = nullptr; 5750 *pOutStreams = nullptr; 5751 5752 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5753 if (!pDoc) 5754 { 5755 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5756 return 0; 5757 } 5758 5759 rtl::Reference<LOKClipboard> xClip(LOKClipboardFactory::getClipboardForCurView()); 5760 5761 css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents(); 5762 SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferable: " << xTransferable); 5763 if (!xTransferable) 5764 { 5765 SetLastExceptionMsg(u"No clipboard content available"_ustr); 5766 return 0; 5767 } 5768 5769 std::vector<OString> aMimeTypes; 5770 if (!pMimeTypes) // everything 5771 { 5772 const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors(); 5773 if (!flavors.getLength()) 5774 { 5775 SetLastExceptionMsg(u"Flavourless selection"_ustr); 5776 return 0; 5777 } 5778 for (const auto &it : flavors) 5779 aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8)); 5780 } 5781 else 5782 { 5783 for (size_t i = 0; pMimeTypes[i]; ++i) 5784 aMimeTypes.push_back(OString(pMimeTypes[i])); 5785 } 5786 5787 *pOutCount = aMimeTypes.size(); 5788 *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t))); 5789 *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *))); 5790 *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *))); 5791 for (size_t i = 0; i < aMimeTypes.size(); ++i) 5792 { 5793 if (aMimeTypes[i] == "text/plain;charset=utf-16") 5794 (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8"); 5795 else 5796 (*pOutMimeTypes)[i] = convertOString(aMimeTypes[i]); 5797 5798 OString aRet; 5799 bool bSuccess = getFromTransferable(xTransferable, (*pOutMimeTypes)[i], aRet); 5800 if (!bSuccess || aRet.getLength() < 1) 5801 { 5802 (*pOutSizes)[i] = 0; 5803 (*pOutStreams)[i] = nullptr; 5804 } 5805 else 5806 { 5807 (*pOutSizes)[i] = aRet.getLength(); 5808 (*pOutStreams)[i] = convertOString(aRet); 5809 } 5810 } 5811 5812 return 1; 5813 #endif 5814 } 5815 5816 static int doc_setClipboard(LibreOfficeKitDocument* pThis, 5817 const size_t nInCount, 5818 const char **pInMimeTypes, 5819 const size_t *pInSizes, 5820 const char **pInStreams) 5821 { 5822 #ifdef IOS 5823 (void) pThis; 5824 (void) nInCount; 5825 (void) pInMimeTypes; 5826 (void) pInSizes; 5827 (void) pInStreams; 5828 #else 5829 comphelper::ProfileZone aZone("doc_setClipboard"); 5830 5831 SolarMutexGuard aGuard; 5832 SetLastExceptionMsg(); 5833 5834 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5835 if (!pDoc) 5836 { 5837 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5838 return false; 5839 } 5840 5841 uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams)); 5842 5843 auto xClip = forceSetClipboardForCurrentView(pThis); 5844 xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); 5845 5846 SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable); 5847 5848 if (!pDoc->isMimeTypeSupported()) 5849 { 5850 SetLastExceptionMsg(u"Document doesn't support this mime type"_ustr); 5851 return false; 5852 } 5853 #endif 5854 return true; 5855 } 5856 5857 static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize) 5858 { 5859 comphelper::ProfileZone aZone("doc_paste"); 5860 5861 SolarMutexGuard aGuard; 5862 5863 const char *pInMimeTypes[1]; 5864 const char *pInStreams[1]; 5865 size_t pInSizes[1]; 5866 pInMimeTypes[0] = pMimeType; 5867 pInSizes[0] = nSize; 5868 pInStreams[0] = pData; 5869 5870 if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams)) 5871 return false; 5872 5873 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence( 5874 { 5875 {"AnchorType", uno::Any(static_cast<sal_uInt16>(css::text::TextContentAnchorType_AS_CHARACTER))}, 5876 {"IgnoreComments", uno::Any(true)}, 5877 })); 5878 if (!comphelper::dispatchCommand(u".uno:Paste"_ustr, aPropertyValues)) 5879 { 5880 SetLastExceptionMsg(u"Failed to dispatch the .uno: command"_ustr); 5881 return false; 5882 } 5883 5884 return true; 5885 } 5886 5887 static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY) 5888 { 5889 comphelper::ProfileZone aZone("doc_setGraphicSelection"); 5890 5891 SolarMutexGuard aGuard; 5892 SetLastExceptionMsg(); 5893 5894 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5895 if (!pDoc) 5896 { 5897 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5898 return; 5899 } 5900 5901 pDoc->setGraphicSelection(nType, nX, nY); 5902 } 5903 5904 static void doc_resetSelection(LibreOfficeKitDocument* pThis) 5905 { 5906 comphelper::ProfileZone aZone("doc_resetSelection"); 5907 5908 SolarMutexGuard aGuard; 5909 SetLastExceptionMsg(); 5910 5911 ITiledRenderable* pDoc = getTiledRenderable(pThis); 5912 if (!pDoc) 5913 { 5914 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 5915 return; 5916 } 5917 5918 pDoc->resetSelection(); 5919 } 5920 5921 static char* getDocReadOnly(LibreOfficeKitDocument* pThis) 5922 { 5923 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 5924 if (!pDocument) 5925 return nullptr; 5926 5927 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 5928 if (!pBaseModel) 5929 return nullptr; 5930 5931 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 5932 if (!pObjectShell) 5933 return nullptr; 5934 5935 boost::property_tree::ptree aTree; 5936 aTree.put("commandName", ".uno:ReadOnly"); 5937 aTree.put("success", pObjectShell->IsLoadReadonly()); 5938 5939 std::stringstream aStream; 5940 boost::property_tree::write_json(aStream, aTree); 5941 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1)); 5942 if (!pJson) 5943 return nullptr; 5944 5945 strcpy(pJson, aStream.str().c_str()); 5946 pJson[aStream.str().size()] = '\0'; 5947 return pJson; 5948 } 5949 5950 static void addLocale(boost::property_tree::ptree& rValues, css::lang::Locale const & rLocale) 5951 { 5952 boost::property_tree::ptree aChild; 5953 const LanguageTag aLanguageTag( rLocale ); 5954 OUString sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType()); 5955 if (sLanguage.endsWith("}")) 5956 return; 5957 5958 sLanguage += ";" + aLanguageTag.getBcp47(false); 5959 aChild.put("", sLanguage.toUtf8()); 5960 rValues.push_back(std::make_pair("", aChild)); 5961 } 5962 5963 static char* getLanguages(const char* pCommand) 5964 { 5965 css::uno::Sequence< css::lang::Locale > aLocales; 5966 css::uno::Sequence< css::lang::Locale > aGrammarLocales; 5967 5968 if (xContext.is()) 5969 { 5970 // SpellChecker 5971 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext); 5972 if (xLangSrv.is()) 5973 { 5974 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker(); 5975 if (xSpell.is()) 5976 aLocales = xSpell->getLocales(); 5977 } 5978 5979 // LanguageTool 5980 if (LanguageToolCfg::IsEnabled::get()) 5981 { 5982 uno::Reference< linguistic2::XProofreader > xGC( 5983 xContext->getServiceManager()->createInstanceWithContext(u"org.openoffice.lingu.LanguageToolGrammarChecker"_ustr, xContext), 5984 uno::UNO_QUERY_THROW ); 5985 uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xGC, uno::UNO_QUERY_THROW ); 5986 aGrammarLocales = xSuppLoc->getLocales(); 5987 } 5988 } 5989 5990 boost::property_tree::ptree aTree; 5991 aTree.put("commandName", pCommand); 5992 boost::property_tree::ptree aValues; 5993 for (css::lang::Locale const& rLocale : aLocales) 5994 addLocale(aValues, rLocale); 5995 for (css::lang::Locale const& rLocale : aGrammarLocales) 5996 addLocale(aValues, rLocale); 5997 aTree.add_child("commandValues", aValues); 5998 std::stringstream aStream; 5999 boost::property_tree::write_json(aStream, aTree); 6000 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1)); 6001 assert(pJson); // Don't handle OOM conditions 6002 strcpy(pJson, aStream.str().c_str()); 6003 pJson[aStream.str().size()] = '\0'; 6004 return pJson; 6005 } 6006 6007 static char* getFonts (const char* pCommand) 6008 { 6009 SfxObjectShell* pDocSh = SfxObjectShell::Current(); 6010 if (!pDocSh) 6011 return nullptr; 6012 const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>( 6013 pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST)); 6014 const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr; 6015 6016 boost::property_tree::ptree aTree; 6017 aTree.put("commandName", pCommand); 6018 boost::property_tree::ptree aValues; 6019 if ( pList ) 6020 { 6021 sal_uInt16 nFontCount = pList->GetFontNameCount(); 6022 for (sal_uInt16 i = 0; i < nFontCount; ++i) 6023 { 6024 boost::property_tree::ptree aChildren; 6025 const FontMetric& rFontMetric = pList->GetFontName(i); 6026 const int* pAry = FontList::GetStdSizeAry(); 6027 sal_uInt16 nSizeCount = 0; 6028 while (pAry[nSizeCount]) 6029 { 6030 boost::property_tree::ptree aChild; 6031 aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10); 6032 aChildren.push_back(std::make_pair("", aChild)); 6033 nSizeCount++; 6034 } 6035 aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren); 6036 } 6037 } 6038 aTree.add_child("commandValues", aValues); 6039 std::stringstream aStream; 6040 boost::property_tree::write_json(aStream, aTree); 6041 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1)); 6042 assert(pJson); // Don't handle OOM conditions 6043 strcpy(pJson, aStream.str().c_str()); 6044 pJson[aStream.str().size()] = '\0'; 6045 return pJson; 6046 } 6047 6048 static char* getFontSubset (std::string_view aFontName) 6049 { 6050 OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8)); 6051 6052 boost::property_tree::ptree aTree; 6053 aTree.put("commandName", ".uno:FontSubset"); 6054 boost::property_tree::ptree aValues; 6055 6056 if (const vcl::Font* pFont = FindFont(aFoundFont)) 6057 { 6058 FontCharMapRef xFontCharMap (new FontCharMap()); 6059 auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA)); 6060 6061 aDevice->SetFont(*pFont); 6062 aDevice->GetFontCharMap(xFontCharMap); 6063 SubsetMap aSubMap(xFontCharMap); 6064 6065 for (auto const& subset : aSubMap.GetSubsetMap()) 6066 { 6067 boost::property_tree::ptree aChild; 6068 aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin()))); 6069 aValues.push_back(std::make_pair("", aChild)); 6070 } 6071 } 6072 6073 aTree.add_child("commandValues", aValues); 6074 std::stringstream aStream; 6075 boost::property_tree::write_json(aStream, aTree); 6076 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1)); 6077 assert(pJson); // Don't handle OOM conditions 6078 strcpy(pJson, aStream.str().c_str()); 6079 pJson[aStream.str().size()] = '\0'; 6080 return pJson; 6081 } 6082 6083 static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand) 6084 { 6085 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6086 6087 boost::property_tree::ptree aTree; 6088 aTree.put("commandName", pCommand); 6089 uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY); 6090 const uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies(); 6091 const uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames(); 6092 6093 static constexpr OUString aWriterStyles[] = 6094 { 6095 u"Text body"_ustr, 6096 u"Quotations"_ustr, 6097 u"Title"_ustr, 6098 u"Subtitle"_ustr, 6099 u"Heading 1"_ustr, 6100 u"Heading 2"_ustr, 6101 u"Heading 3"_ustr, 6102 }; 6103 6104 // We need to keep a list of the default style names 6105 // in order to filter these out later when processing 6106 // the full list of styles. 6107 std::set<OUString> aDefaultStyleNames; 6108 6109 boost::property_tree::ptree aValues; 6110 for (OUString const & sStyleFam : aStyleFamilies) 6111 { 6112 boost::property_tree::ptree aChildren; 6113 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY); 6114 6115 // Writer provides a huge number of styles, we have a list of 7 "default" styles which 6116 // should be shown in the normal dropdown, which we should add to the start of the list 6117 // to simplify their selection. 6118 if (sStyleFam == "ParagraphStyles" 6119 && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT) 6120 { 6121 for (const OUString& rStyle: aWriterStyles) 6122 { 6123 aDefaultStyleNames.insert( rStyle ); 6124 6125 boost::property_tree::ptree aChild; 6126 aChild.put("", rStyle.toUtf8()); 6127 aChildren.push_back(std::make_pair("", aChild)); 6128 } 6129 } 6130 6131 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames(); 6132 for (const OUString& rStyle: aStyles ) 6133 { 6134 // Filter out the default styles - they are already at the top 6135 // of the list 6136 if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() || 6137 (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) ) 6138 { 6139 boost::property_tree::ptree aChild; 6140 aChild.put("", rStyle.toUtf8()); 6141 aChildren.push_back(std::make_pair("", aChild)); 6142 } 6143 } 6144 aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren); 6145 } 6146 6147 // Header & Footer Styles 6148 { 6149 boost::property_tree::ptree aChild; 6150 boost::property_tree::ptree aChildren; 6151 static constexpr OUString sPageStyles(u"PageStyles"_ustr); 6152 uno::Reference<beans::XPropertySet> xProperty; 6153 uno::Reference<container::XNameContainer> xContainer; 6154 6155 if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer)) 6156 { 6157 const uno::Sequence<OUString> aSeqNames = xContainer->getElementNames(); 6158 for (OUString const & sName : aSeqNames) 6159 { 6160 bool bIsPhysical; 6161 xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY); 6162 if (xProperty.is() && (xProperty->getPropertyValue(u"IsPhysical"_ustr) >>= bIsPhysical) && bIsPhysical) 6163 { 6164 OUString displayName; 6165 xProperty->getPropertyValue(u"DisplayName"_ustr) >>= displayName; 6166 aChild.put("", displayName.toUtf8()); 6167 aChildren.push_back(std::make_pair("", aChild)); 6168 } 6169 } 6170 aValues.add_child("HeaderFooter", aChildren); 6171 } 6172 } 6173 6174 { 6175 boost::property_tree::ptree aCommandList; 6176 6177 { 6178 boost::property_tree::ptree aChild; 6179 6180 OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM); 6181 6182 boost::property_tree::ptree aName; 6183 aName.put("", sClearFormat.toUtf8()); 6184 aChild.push_back(std::make_pair("text", aName)); 6185 6186 boost::property_tree::ptree aCommand; 6187 aCommand.put("", ".uno:ResetAttributes"); 6188 aChild.push_back(std::make_pair("id", aCommand)); 6189 6190 aCommandList.push_back(std::make_pair("", aChild)); 6191 } 6192 6193 aValues.add_child("Commands", aCommandList); 6194 } 6195 6196 aTree.add_child("commandValues", aValues); 6197 std::stringstream aStream; 6198 boost::property_tree::write_json(aStream, aTree); 6199 char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1)); 6200 assert(pJson); // Don't handle OOM conditions 6201 strcpy(pJson, aStream.str().c_str()); 6202 pJson[aStream.str().size()] = '\0'; 6203 return pJson; 6204 } 6205 6206 namespace { 6207 6208 enum class UndoOrRedo 6209 { 6210 UNDO, 6211 REDO 6212 }; 6213 6214 } 6215 6216 /// Returns the JSON representation of either an undo or a redo stack. 6217 static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand) 6218 { 6219 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6220 6221 auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 6222 if (!pBaseModel) 6223 return nullptr; 6224 6225 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 6226 if (!pObjectShell) 6227 return nullptr; 6228 6229 SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager(); 6230 if (!pUndoManager) 6231 return nullptr; 6232 6233 OUString aString; 6234 if (eCommand == UndoOrRedo::UNDO) 6235 aString = pUndoManager->GetUndoActionsInfo(); 6236 else 6237 aString = pUndoManager->GetRedoActionsInfo(); 6238 char* pJson = convertOUString(aString); 6239 return pJson; 6240 } 6241 6242 /// Returns the JSON representation of the redline stack. 6243 static char* getTrackedChanges(LibreOfficeKitDocument* pThis) 6244 { 6245 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6246 6247 uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY); 6248 tools::JsonWriter aJson; 6249 // We want positions of the track changes also which is not possible from 6250 // UNO. Enable positioning information for text documents only for now, so 6251 // construct the tracked changes JSON from inside the sw/, not here using UNO 6252 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is()) 6253 { 6254 auto redlinesNode = aJson.startArray("redlines"); 6255 uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration(); 6256 for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex) 6257 { 6258 uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY); 6259 auto redlineNode = aJson.startStruct(); 6260 aJson.put("index", static_cast<sal_Int32>(nIndex)); 6261 6262 OUString sAuthor; 6263 xRedline->getPropertyValue(u"RedlineAuthor"_ustr) >>= sAuthor; 6264 aJson.put("author", sAuthor); 6265 6266 OUString sType; 6267 xRedline->getPropertyValue(u"RedlineType"_ustr) >>= sType; 6268 aJson.put("type", sType); 6269 6270 OUString sComment; 6271 xRedline->getPropertyValue(u"RedlineComment"_ustr) >>= sComment; 6272 aJson.put("comment", sComment); 6273 6274 OUString sDescription; 6275 xRedline->getPropertyValue(u"RedlineDescription"_ustr) >>= sDescription; 6276 aJson.put("description", sDescription); 6277 6278 util::DateTime aDateTime; 6279 xRedline->getPropertyValue(u"RedlineDateTime"_ustr) >>= aDateTime; 6280 OUString sDateTime = utl::toISO8601(aDateTime); 6281 aJson.put("dateTime", sDateTime); 6282 } 6283 } 6284 else 6285 { 6286 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6287 if (!pDoc) 6288 { 6289 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6290 return nullptr; 6291 } 6292 pDoc->getTrackedChanges(aJson); 6293 } 6294 6295 return convertOString(aJson.finishAndGetAsOString()); 6296 } 6297 6298 6299 /// Returns the JSON representation of the redline author table. 6300 static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis) 6301 { 6302 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6303 if (!pDoc) 6304 { 6305 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6306 return nullptr; 6307 } 6308 tools::JsonWriter aJsonWriter; 6309 pDoc->getTrackedChangeAuthors(aJsonWriter); 6310 return convertOString(aJsonWriter.finishAndGetAsOString()); 6311 } 6312 6313 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand) 6314 { 6315 comphelper::ProfileZone aZone("doc_getCommandValues"); 6316 6317 SolarMutexGuard aGuard; 6318 SetLastExceptionMsg(); 6319 6320 const std::string_view aCommand(pCommand); 6321 static constexpr std::string_view aViewRowColumnHeaders(".uno:ViewRowColumnHeaders"); 6322 static constexpr std::string_view aSheetGeometryData(".uno:SheetGeometryData"); 6323 static constexpr std::string_view aFontSubset(".uno:FontSubset&name="); 6324 6325 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6326 if (!pDoc) 6327 { 6328 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6329 return nullptr; 6330 } 6331 6332 if (aCommand == ".uno:ReadOnly") 6333 { 6334 return getDocReadOnly(pThis); 6335 } 6336 else if (aCommand == ".uno:LanguageStatus") 6337 { 6338 return getLanguages(pCommand); 6339 } 6340 else if (aCommand == ".uno:CharFontName") 6341 { 6342 return getFonts(pCommand); 6343 } 6344 else if (aCommand == ".uno:StyleApply") 6345 { 6346 return getStyles(pThis, pCommand); 6347 } 6348 else if (aCommand == ".uno:Undo") 6349 { 6350 return getUndoOrRedo(pThis, UndoOrRedo::UNDO); 6351 } 6352 else if (aCommand == ".uno:Redo") 6353 { 6354 return getUndoOrRedo(pThis, UndoOrRedo::REDO); 6355 } 6356 else if (aCommand == ".uno:AcceptTrackedChanges") 6357 { 6358 return getTrackedChanges(pThis); 6359 } 6360 else if (aCommand == ".uno:TrackedChangeAuthors") 6361 { 6362 return getTrackedChangeAuthors(pThis); 6363 } 6364 else if (aCommand == ".uno:ViewAnnotations") 6365 { 6366 return getPostIts(pThis); 6367 } 6368 else if (aCommand == ".uno:ViewAnnotationsPosition") 6369 { 6370 return getPostItsPos(pThis); 6371 } 6372 else if (aCommand == ".uno:RulerState") 6373 { 6374 return getRulerState(pThis); 6375 } 6376 else if (aCommand == ".uno:ViewRenderState") 6377 { 6378 return convertOString(pDoc->getViewRenderState()); 6379 } 6380 else if (aCommand.starts_with(aViewRowColumnHeaders)) 6381 { 6382 tools::Rectangle aRectangle; 6383 if (aCommand.size() > aViewRowColumnHeaders.size()) 6384 { 6385 // Command has parameters. 6386 int nX = 0; 6387 int nY = 0; 6388 int nWidth = 0; 6389 int nHeight = 0; 6390 std::string_view aArguments = aCommand.substr(aViewRowColumnHeaders.size() + 1); 6391 sal_Int32 nParamIndex = 0; 6392 do 6393 { 6394 std::string_view aParamToken = o3tl::getToken(aArguments, 0, '&', nParamIndex); 6395 sal_Int32 nIndex = 0; 6396 std::string_view aKey; 6397 std::string_view aValue; 6398 do 6399 { 6400 std::string_view aToken = o3tl::getToken(aParamToken, 0, '=', nIndex); 6401 if (aKey.empty()) 6402 aKey = aToken; 6403 else 6404 aValue = aToken; 6405 } 6406 while (nIndex >= 0); 6407 if (aKey == "x") 6408 nX = o3tl::toInt32(aValue); 6409 else if (aKey == "y") 6410 nY = o3tl::toInt32(aValue); 6411 else if (aKey == "width") 6412 nWidth = o3tl::toInt32(aValue); 6413 else if (aKey == "height") 6414 nHeight = o3tl::toInt32(aValue); 6415 } 6416 while (nParamIndex >= 0); 6417 6418 aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight); 6419 } 6420 6421 tools::JsonWriter aJsonWriter; 6422 pDoc->getRowColumnHeaders(aRectangle, aJsonWriter); 6423 return convertOString(aJsonWriter.finishAndGetAsOString()); 6424 } 6425 else if (aCommand.starts_with(aSheetGeometryData)) 6426 { 6427 bool bColumns = true; 6428 bool bRows = true; 6429 bool bSizes = true; 6430 bool bHidden = true; 6431 bool bFiltered = true; 6432 bool bGroups = true; 6433 if (aCommand.size() > aSheetGeometryData.size()) 6434 { 6435 bColumns = bRows = bSizes = bHidden = bFiltered = bGroups = false; 6436 6437 std::string_view aArguments = aCommand.substr(aSheetGeometryData.size() + 1); 6438 sal_Int32 nParamIndex = 0; 6439 do 6440 { 6441 std::string_view aParamToken = o3tl::getToken(aArguments, 0, '&', nParamIndex); 6442 sal_Int32 nIndex = 0; 6443 std::string_view aKey; 6444 std::string_view aValue; 6445 do 6446 { 6447 std::string_view aToken = o3tl::getToken(aParamToken, 0, '=', nIndex); 6448 if (aKey.empty()) 6449 aKey = aToken; 6450 else 6451 aValue = aToken; 6452 6453 } while (nIndex >= 0); 6454 6455 bool bEnableFlag = aValue.empty() || 6456 o3tl::equalsIgnoreAsciiCase(aValue, "true") || o3tl::toInt32(aValue) > 0; 6457 if (!bEnableFlag) 6458 continue; 6459 6460 if (aKey == "columns") 6461 bColumns = true; 6462 else if (aKey == "rows") 6463 bRows = true; 6464 else if (aKey == "sizes") 6465 bSizes = true; 6466 else if (aKey == "hidden") 6467 bHidden = true; 6468 else if (aKey == "filtered") 6469 bFiltered = true; 6470 else if (aKey == "groups") 6471 bGroups = true; 6472 6473 } while (nParamIndex >= 0); 6474 } 6475 6476 OString aGeomDataStr 6477 = pDoc->getSheetGeometryData(bColumns, bRows, bSizes, bHidden, bFiltered, bGroups); 6478 6479 if (aGeomDataStr.isEmpty()) 6480 return nullptr; 6481 6482 return convertOString(aGeomDataStr); 6483 } 6484 else if (aCommand.starts_with(".uno:CellCursor")) 6485 { 6486 // Ignore command's deprecated parameters. 6487 tools::JsonWriter aJsonWriter; 6488 pDoc->getCellCursor(aJsonWriter); 6489 return convertOString(aJsonWriter.finishAndGetAsOString()); 6490 } 6491 else if (aCommand.starts_with(aFontSubset)) 6492 { 6493 return getFontSubset(aCommand.substr(aFontSubset.size())); 6494 } 6495 else if (pDoc->supportsCommand(INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath())) 6496 { 6497 tools::JsonWriter aJsonWriter; 6498 pDoc->getCommandValues(aJsonWriter, aCommand); 6499 return convertOString(aJsonWriter.finishAndGetAsOString()); 6500 } 6501 else 6502 { 6503 SetLastExceptionMsg(u"Unknown command, no values returned"_ustr); 6504 return nullptr; 6505 } 6506 } 6507 6508 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight, 6509 int nTileTwipWidth, int nTileTwipHeight) 6510 { 6511 comphelper::ProfileZone aZone("doc_setClientZoom"); 6512 6513 SolarMutexGuard aGuard; 6514 SetLastExceptionMsg(); 6515 6516 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6517 if (!pDoc) 6518 { 6519 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6520 return; 6521 } 6522 6523 pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight); 6524 } 6525 6526 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight) 6527 { 6528 comphelper::ProfileZone aZone("doc_setClientVisibleArea"); 6529 6530 SolarMutexGuard aGuard; 6531 SetLastExceptionMsg(); 6532 6533 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6534 if (!pDoc) 6535 { 6536 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6537 return; 6538 } 6539 6540 tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight)); 6541 pDoc->setClientVisibleArea(aRectangle); 6542 } 6543 6544 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden) 6545 { 6546 comphelper::ProfileZone aZone("doc_setOutlineState"); 6547 6548 SolarMutexGuard aGuard; 6549 SetLastExceptionMsg(); 6550 6551 ITiledRenderable* pDoc = getTiledRenderable(pThis); 6552 if (!pDoc) 6553 { 6554 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 6555 return; 6556 } 6557 6558 pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden); 6559 } 6560 6561 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, 6562 const char* pOptions) 6563 { 6564 comphelper::ProfileZone aZone("doc_createView"); 6565 6566 SolarMutexGuard aGuard; 6567 SetLastExceptionMsg(); 6568 6569 OUString aOptions = getUString(pOptions); 6570 const OUString aLanguage = extractParameter(aOptions, u"Language"); 6571 6572 if (!aLanguage.isEmpty()) 6573 { 6574 // Set the LOK language tag, used for dialog tunneling. 6575 comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage)); 6576 comphelper::LibreOfficeKit::setLocale(LanguageTag(aLanguage)); 6577 } 6578 6579 const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor"); 6580 SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor); 6581 6582 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6583 const int nId = SfxLokHelper::createView(pDocument->mnDocumentId); 6584 6585 vcl::lok::numberOfViewsChanged(SfxLokHelper::getViewsCount(pDocument->mnDocumentId)); 6586 6587 #ifdef IOS 6588 (void) pThis; 6589 #else 6590 forceSetClipboardForCurrentView(pThis); 6591 #endif 6592 6593 return nId; 6594 } 6595 6596 static int doc_createView(LibreOfficeKitDocument* pThis) 6597 { 6598 return doc_createViewWithOptions(pThis, nullptr); // No options. 6599 } 6600 6601 static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId) 6602 { 6603 comphelper::ProfileZone aZone("doc_destroyView"); 6604 6605 SolarMutexGuard aGuard; 6606 SetLastExceptionMsg(); 6607 6608 #ifndef IOS 6609 LOKClipboardFactory::releaseClipboardForView(nId); 6610 #endif 6611 6612 SfxLokHelper::destroyView(nId); 6613 6614 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6615 vcl::lok::numberOfViewsChanged(SfxLokHelper::getViewsCount(pDocument->mnDocumentId)); 6616 } 6617 6618 static void doc_setView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId) 6619 { 6620 comphelper::ProfileZone aZone("doc_setView"); 6621 6622 SolarMutexGuard aGuard; 6623 SetLastExceptionMsg(); 6624 6625 SfxLokHelper::setView(nId); 6626 } 6627 6628 static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/) 6629 { 6630 comphelper::ProfileZone aZone("doc_getView"); 6631 6632 SolarMutexGuard aGuard; 6633 SetLastExceptionMsg(); 6634 6635 return SfxLokHelper::getView(); 6636 } 6637 6638 static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis) 6639 { 6640 comphelper::ProfileZone aZone("doc_getViewsCount"); 6641 6642 SolarMutexGuard aGuard; 6643 SetLastExceptionMsg(); 6644 6645 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6646 return SfxLokHelper::getViewsCount(pDocument->mnDocumentId); 6647 } 6648 6649 static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int* pArray, size_t nSize) 6650 { 6651 comphelper::ProfileZone aZone("doc_getViewsIds"); 6652 6653 SolarMutexGuard aGuard; 6654 SetLastExceptionMsg(); 6655 6656 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6657 return SfxLokHelper::getViewIds(pDocument->mnDocumentId, pArray, nSize); 6658 } 6659 6660 static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language) 6661 { 6662 comphelper::ProfileZone aZone("doc_setViewLanguage"); 6663 6664 SolarMutexGuard aGuard; 6665 SetLastExceptionMsg(); 6666 6667 OUString sLanguage = OStringToOUString(language, RTL_TEXTENCODING_UTF8); 6668 SfxLokHelper::setViewLanguage(nId, sLanguage); 6669 SfxLokHelper::setViewLocale(nId, sLanguage); 6670 } 6671 6672 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis, 6673 const char* pFontName, 6674 const char* pChar, 6675 int* pFontWidth, 6676 int* pFontHeight) 6677 { 6678 return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0); 6679 } 6680 6681 unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, 6682 const char* pFontName, 6683 const char* pChar, 6684 int* pFontWidth, 6685 int* pFontHeight, 6686 int pOrientation) 6687 { 6688 comphelper::ProfileZone aZone("doc_renderFont"); 6689 6690 SolarMutexGuard aGuard; 6691 SetLastExceptionMsg(); 6692 6693 const int nDefaultFontSize = 25; 6694 6695 auto aFont = FindFont_FallbackToDefault(OStringToOUString(pFontName, RTL_TEXTENCODING_UTF8)); 6696 6697 OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8)); 6698 if (aText.isEmpty()) 6699 aText = aFont.GetFamilyName(); 6700 6701 auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA)); 6702 ::tools::Rectangle aRect; 6703 aFont.SetFontSize(Size(0, nDefaultFontSize)); 6704 aFont.SetOrientation(Degree10(pOrientation)); 6705 aDevice->SetFont(aFont); 6706 aDevice->GetTextBoundRect(aRect, aText); 6707 if (aRect.IsEmpty()) 6708 return nullptr; 6709 6710 int nFontWidth = aRect.Right() + 1; 6711 int nFontHeight = aRect.Bottom() + 1; 6712 6713 if (nFontWidth <= 0 || nFontHeight <= 0) 6714 return nullptr; 6715 6716 if (*pFontWidth > 0 && *pFontHeight > 0) 6717 { 6718 double fScaleX = *pFontWidth / static_cast<double>(nFontWidth) / 1.5; 6719 double fScaleY = *pFontHeight / static_cast<double>(nFontHeight) / 1.5; 6720 6721 double fScale = std::min(fScaleX, fScaleY); 6722 6723 if (fScale >= 1.0) 6724 { 6725 int nFontSize = fScale * nDefaultFontSize; 6726 aFont.SetFontSize(Size(0, nFontSize)); 6727 aDevice->SetFont(aFont); 6728 } 6729 6730 aRect = tools::Rectangle(0, 0, *pFontWidth, *pFontHeight); 6731 6732 nFontWidth = *pFontWidth; 6733 nFontHeight = *pFontHeight; 6734 6735 } 6736 6737 unsigned char* pBuffer = static_cast<unsigned char*>(malloc(4 * nFontWidth * nFontHeight)); 6738 if (!pBuffer) 6739 return nullptr; 6740 6741 memset(pBuffer, 0, nFontWidth * nFontHeight * 4); 6742 aDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); 6743 aDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer( 6744 Size(nFontWidth, nFontHeight), Fraction(1.0), Point(), 6745 pBuffer); 6746 6747 if (*pFontWidth > 0 && *pFontHeight > 0) 6748 { 6749 DrawTextFlags const nStyle = 6750 DrawTextFlags::Center 6751 | DrawTextFlags::VCenter 6752 | DrawTextFlags::MultiLine 6753 | DrawTextFlags::WordBreak;// | DrawTextFlags::WordBreakHyphenation ; 6754 6755 aDevice->DrawText(aRect, aText, nStyle); 6756 } 6757 else 6758 { 6759 *pFontWidth = nFontWidth; 6760 *pFontHeight = nFontHeight; 6761 6762 aDevice->DrawText(Point(0,0), aText); 6763 } 6764 6765 6766 return pBuffer; 6767 } 6768 6769 6770 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, 6771 unsigned char* pBuffer, 6772 const int nX, const int nY, 6773 const int nWidth, const int nHeight) 6774 { 6775 doc_paintWindowDPI(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, 1.0); 6776 } 6777 6778 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, 6779 unsigned char* pBuffer, 6780 const int nX, const int nY, 6781 const int nWidth, const int nHeight, 6782 const double fDPIScale) 6783 { 6784 doc_paintWindowForView(pThis, nLOKWindowId, pBuffer, nX, nY, nWidth, nHeight, fDPIScale, -1); 6785 } 6786 6787 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, 6788 unsigned char* pBuffer, const int nX, const int nY, 6789 const int nWidth, const int nHeight, 6790 const double fDPIScale, int viewId) 6791 { 6792 comphelper::ProfileZone aZone("doc_paintWindowDPI"); 6793 6794 SolarMutexGuard aGuard; 6795 SetLastExceptionMsg(); 6796 6797 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 6798 if (!pWindow) 6799 { 6800 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 6801 return; 6802 } 6803 6804 // Used to avoid work in setView if set. 6805 comphelper::LibreOfficeKit::setDialogPainting(true); 6806 6807 if (viewId >= 0) 6808 doc_setView(pThis, viewId); 6809 6810 // Setup cairo (or CoreGraphics, in the iOS case) to draw with the changed DPI scale (and return 6811 // back to 1.0 when the painting finishes) 6812 comphelper::ScopeGuard dpiScaleGuard([]() { comphelper::LibreOfficeKit::setDPIScale(1.0); }); 6813 comphelper::LibreOfficeKit::setDPIScale(fDPIScale); 6814 6815 #if defined(IOS) 6816 // Onine uses the LOK_TILEMODE_RGBA by default so flip the normal flags 6817 // to kCGImageAlphaNoneSkipLast | kCGImageByteOrder32Big 6818 CGContextRef cgc = CGBitmapContextCreate(pBuffer, nWidth, nHeight, 8, nWidth*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipLast | kCGImageByteOrder32Big); 6819 6820 CGContextTranslateCTM(cgc, 0, nHeight); 6821 CGContextScaleCTM(cgc, fDPIScale, -fDPIScale); 6822 6823 SystemGraphicsData aData; 6824 aData.rCGContext = cgc; 6825 6826 ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::WITHOUT_ALPHA); 6827 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); 6828 6829 pDevice->SetOutputSizePixel(Size(nWidth, nHeight)); 6830 6831 MapMode aMapMode(pDevice->GetMapMode()); 6832 aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale))); 6833 pDevice->SetMapMode(aMapMode); 6834 6835 pWindow->PaintToDevice(pDevice.get(), Point(0, 0)); 6836 6837 CGContextRelease(cgc); 6838 6839 #else 6840 6841 ScopedVclPtrInstance<VirtualDevice> pDevice(DeviceFormat::WITHOUT_ALPHA); 6842 pDevice->SetBackground(Wallpaper(COL_TRANSPARENT)); 6843 6844 pDevice->SetOutputSizePixelScaleOffsetAndLOKBuffer(Size(nWidth, nHeight), Fraction(1.0), Point(), pBuffer); 6845 6846 MapMode aMapMode(pDevice->GetMapMode()); 6847 aMapMode.SetOrigin(Point(-(nX / fDPIScale), -(nY / fDPIScale))); 6848 pDevice->SetMapMode(aMapMode); 6849 6850 pWindow->PaintToDevice(pDevice.get(), Point(0, 0)); 6851 #endif 6852 6853 comphelper::LibreOfficeKit::setDialogPainting(false); 6854 } 6855 6856 static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nAction, const char* pData) 6857 { 6858 comphelper::ProfileZone aZone("doc_postWindow"); 6859 6860 SolarMutexGuard aGuard; 6861 SetLastExceptionMsg(); 6862 6863 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 6864 if (!pWindow) 6865 { 6866 SetLastExceptionMsg(u"Document doesn't support dialog rendering, or window not found."_ustr); 6867 return; 6868 } 6869 6870 if (nAction == LOK_WINDOW_CLOSE) 6871 { 6872 vcl::CloseTopLevel(pWindow); 6873 } 6874 else if (nAction == LOK_WINDOW_PASTE) 6875 { 6876 #ifndef IOS 6877 OUString aMimeType; 6878 css::uno::Sequence<sal_Int8> aData; 6879 std::vector<beans::PropertyValue> aArgs(jsonToPropertyValuesVector(pData)); 6880 { 6881 aArgs.size() == 2 && 6882 aArgs[0].Name == "MimeType" && (aArgs[0].Value >>= aMimeType) && 6883 aArgs[1].Name == "Data" && (aArgs[1].Value >>= aData); 6884 } 6885 6886 if (!aMimeType.isEmpty() && aData.hasElements()) 6887 { 6888 uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(aMimeType, aData)); 6889 uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(new LOKClipboard); 6890 xClipboard->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>()); 6891 pWindow->SetClipboard(xClipboard); 6892 6893 KeyEvent aEvent(0, KEY_PASTE, 0); 6894 Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent); 6895 } 6896 else 6897 SetLastExceptionMsg(u"Window command 'paste': wrong parameters."_ustr); 6898 #else 6899 (void) pData; 6900 assert(!"doc_postWindow() with LOK_WINDOW_PASTE should not be called on iOS"); 6901 #endif 6902 } 6903 } 6904 6905 // CERTIFICATE AND DOCUMENT SIGNING 6906 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis, 6907 const unsigned char* pCertificateBinary, const int nCertificateBinarySize, 6908 const unsigned char* pPrivateKeyBinary, const int nPrivateKeySize) 6909 { 6910 comphelper::ProfileZone aZone("doc_insertCertificate"); 6911 6912 if (!xContext.is()) 6913 return false; 6914 6915 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6916 6917 if (!pDocument->mxComponent.is()) 6918 return false; 6919 6920 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 6921 if (!pBaseModel) 6922 return false; 6923 6924 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 6925 6926 if (!pObjectShell) 6927 return false; 6928 6929 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext); 6930 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString()); 6931 if (!xSecurityContext.is()) 6932 return false; 6933 6934 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); 6935 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY); 6936 6937 if (!xCertificateCreator.is()) 6938 return false; 6939 6940 uno::Sequence<sal_Int8> aCertificateSequence; 6941 6942 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize); 6943 std::string aCertificateBase64String = extractCertificate(aCertificateString); 6944 if (!aCertificateBase64String.empty()) 6945 { 6946 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String); 6947 comphelper::Base64::decode(aCertificateSequence, aBase64OUString); 6948 } 6949 else 6950 { 6951 aCertificateSequence.realloc(nCertificateBinarySize); 6952 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray()); 6953 } 6954 6955 uno::Sequence<sal_Int8> aPrivateKeySequence; 6956 std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeySize); 6957 std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString); 6958 if (!aPrivateKeyBase64String.empty()) 6959 { 6960 OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String); 6961 comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString); 6962 } 6963 else 6964 { 6965 aPrivateKeySequence.realloc(nPrivateKeySize); 6966 std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeySize, aPrivateKeySequence.getArray()); 6967 } 6968 6969 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence); 6970 6971 if (!xCertificate.is()) 6972 return false; 6973 6974 SolarMutexGuard aGuard; 6975 6976 return pObjectShell->SignDocumentContentUsingCertificate(xCertificate); 6977 } 6978 6979 static bool doc_addCertificate(LibreOfficeKitDocument* pThis, 6980 const unsigned char* pCertificateBinary, const int nCertificateBinarySize) 6981 { 6982 comphelper::ProfileZone aZone("doc_addCertificate"); 6983 6984 if (!xContext.is()) 6985 return false; 6986 6987 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 6988 6989 if (!pDocument->mxComponent.is()) 6990 return false; 6991 6992 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 6993 if (!pBaseModel) 6994 return false; 6995 6996 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 6997 6998 if (!pObjectShell) 6999 return false; 7000 7001 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext); 7002 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString()); 7003 if (!xSecurityContext.is()) 7004 return false; 7005 7006 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); 7007 uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY); 7008 7009 if (!xCertificateCreator.is()) 7010 return false; 7011 7012 uno::Sequence<sal_Int8> aCertificateSequence; 7013 7014 std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize); 7015 std::string aCertificateBase64String = extractCertificate(aCertificateString); 7016 if (!aCertificateBase64String.empty()) 7017 { 7018 OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String); 7019 comphelper::Base64::decode(aCertificateSequence, aBase64OUString); 7020 } 7021 else 7022 { 7023 aCertificateSequence.realloc(nCertificateBinarySize); 7024 std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.getArray()); 7025 } 7026 7027 uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->addDERCertificateToTheDatabase(aCertificateSequence, u"TCu,Cu,Tu"_ustr); 7028 7029 if (!xCertificate.is()) 7030 return false; 7031 7032 SAL_INFO("lok", "Certificate Added = IssuerName: " << xCertificate->getIssuerName() << " SubjectName: " << xCertificate->getSubjectName()); 7033 7034 return true; 7035 } 7036 7037 static int doc_getSignatureState(LibreOfficeKitDocument* pThis) 7038 { 7039 comphelper::ProfileZone aZone("doc_getSignatureState"); 7040 7041 LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); 7042 7043 if (!pDocument->mxComponent.is()) 7044 return int(SignatureState::UNKNOWN); 7045 7046 SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); 7047 if (!pBaseModel) 7048 return int(SignatureState::UNKNOWN); 7049 7050 SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); 7051 if (!pObjectShell) 7052 return int(SignatureState::UNKNOWN); 7053 7054 SolarMutexGuard aGuard; 7055 7056 pObjectShell->RecheckSignature(false); 7057 7058 return int(pObjectShell->GetDocumentSignatureState()); 7059 } 7060 7061 static void doc_resizeWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, 7062 const int nWidth, const int nHeight) 7063 { 7064 SolarMutexGuard aGuard; 7065 SetLastExceptionMsg(); 7066 7067 VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId); 7068 if (!pWindow) 7069 { 7070 SetLastExceptionMsg(u"Document doesn't support dialog resizing, or window not found."_ustr); 7071 return; 7072 } 7073 7074 pWindow->SetSizePixel(Size(nWidth, nHeight)); 7075 } 7076 7077 static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char* pFunctionName) 7078 { 7079 SolarMutexGuard aGuard; 7080 SetLastExceptionMsg(); 7081 7082 ITiledRenderable* pDoc = getTiledRenderable(pThis); 7083 if (!pDoc) 7084 { 7085 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 7086 return; 7087 } 7088 7089 pDoc->completeFunction(OUString::fromUtf8(pFunctionName)); 7090 } 7091 7092 7093 static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis, const char* pArguments) 7094 { 7095 SolarMutexGuard aGuard; 7096 7097 // Supported in Writer only 7098 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) 7099 return; 7100 7101 StringMap aMap(jsdialog::jsonToStringMap(pArguments)); 7102 ITiledRenderable* pDoc = getTiledRenderable(pThis); 7103 if (!pDoc) 7104 { 7105 SetLastExceptionMsg(u"Document doesn't support tiled rendering!"_ustr); 7106 return; 7107 } 7108 7109 // Sanity check 7110 if (aMap.find(u"type"_ustr) == aMap.end() || aMap.find(u"cmd"_ustr) == aMap.end()) 7111 { 7112 SetLastExceptionMsg(u"Wrong arguments for sendFormFieldEvent!"_ustr); 7113 return; 7114 } 7115 7116 pDoc->executeFromFieldEvent(aMap); 7117 } 7118 7119 static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis, 7120 const char* pSearchResult, unsigned char** pBitmapBuffer, 7121 int* pWidth, int* pHeight, size_t* pByteSize) 7122 { 7123 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) 7124 return false; 7125 7126 if (pBitmapBuffer == nullptr) 7127 return false; 7128 7129 if (!pSearchResult || pSearchResult[0] == '\0') 7130 return false; 7131 7132 ITiledRenderable* pDoc = getTiledRenderable(pThis); 7133 if (!pDoc) 7134 { 7135 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 7136 return false; 7137 } 7138 7139 auto aRectangleVector = pDoc->getSearchResultRectangles(pSearchResult); 7140 7141 // combine into a rectangle union 7142 basegfx::B2DRange aRangeUnion; 7143 for (basegfx::B2DRange const & rRange : aRectangleVector) 7144 { 7145 aRangeUnion.expand(rRange); 7146 } 7147 7148 int aPixelWidth = o3tl::convert(aRangeUnion.getWidth(), o3tl::Length::twip, o3tl::Length::px); 7149 int aPixelHeight = o3tl::convert(aRangeUnion.getHeight(), o3tl::Length::twip, o3tl::Length::px); 7150 7151 size_t nByteSize = aPixelWidth * aPixelHeight * 4; 7152 7153 *pWidth = aPixelWidth; 7154 *pHeight = aPixelHeight; 7155 *pByteSize = nByteSize; 7156 7157 auto* pBuffer = static_cast<unsigned char*>(std::malloc(nByteSize)); 7158 7159 doc_paintTile(pThis, pBuffer, 7160 aPixelWidth, aPixelHeight, 7161 aRangeUnion.getMinX(), aRangeUnion.getMinY(), 7162 aRangeUnion.getWidth(), aRangeUnion.getHeight()); 7163 7164 *pBitmapBuffer = pBuffer; 7165 7166 return true; 7167 } 7168 7169 static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments) 7170 { 7171 SolarMutexGuard aGuard; 7172 7173 // Supported in Writer only 7174 if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) 7175 { 7176 return; 7177 } 7178 7179 StringMap aMap(jsdialog::jsonToStringMap(pArguments)); 7180 ITiledRenderable* pDoc = getTiledRenderable(pThis); 7181 if (!pDoc) 7182 { 7183 SetLastExceptionMsg(u"Document doesn't support tiled rendering"_ustr); 7184 return; 7185 } 7186 7187 // Sanity check 7188 if (aMap.find(u"type"_ustr) == aMap.end()) 7189 { 7190 SetLastExceptionMsg(u"Missing 'type' argument for sendContentControlEvent"_ustr); 7191 return; 7192 } 7193 7194 pDoc->executeContentControlEvent(aMap); 7195 } 7196 7197 static void doc_setViewTimezone(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, 7198 const char* pTimezone) 7199 { 7200 comphelper::ProfileZone aZone("doc_setViewTimezone"); 7201 7202 SolarMutexGuard aGuard; 7203 SetLastExceptionMsg(); 7204 7205 // Leave the default if we get a null timezone. 7206 if (pTimezone) 7207 { 7208 OUString sTimezone = OStringToOUString(pTimezone, RTL_TEXTENCODING_UTF8); 7209 SfxLokHelper::setViewTimezone(nId, true, sTimezone); 7210 } 7211 } 7212 7213 static void doc_setAccessibilityState(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId, bool nEnabled) 7214 { 7215 SolarMutexGuard aGuard; 7216 7217 int nDocType = getDocumentType(pThis); 7218 if (!(nDocType == LOK_DOCTYPE_TEXT || nDocType == LOK_DOCTYPE_PRESENTATION || nDocType == LOK_DOCTYPE_SPREADSHEET)) 7219 return; 7220 7221 SfxLokHelper::setAccessibilityState(nId, nEnabled); 7222 } 7223 7224 static char* lo_getError (LibreOfficeKit *pThis) 7225 { 7226 comphelper::ProfileZone aZone("lo_getError"); 7227 7228 SolarMutexGuard aGuard; 7229 7230 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 7231 return convertOUString(pLib->maLastExceptionMsg); 7232 } 7233 7234 static void lo_freeError(char* pFree) 7235 { 7236 free(pFree); 7237 } 7238 7239 static char* lo_getFilterTypes(LibreOfficeKit* pThis) 7240 { 7241 SolarMutexGuard aGuard; 7242 SetLastExceptionMsg(); 7243 7244 LibLibreOffice_Impl* pImpl = static_cast<LibLibreOffice_Impl*>(pThis); 7245 7246 if (!xSFactory.is()) 7247 xSFactory = comphelper::getProcessServiceFactory(); 7248 7249 if (!xSFactory.is()) 7250 { 7251 pImpl->maLastExceptionMsg = u"Service factory is not available"_ustr; 7252 return nullptr; 7253 } 7254 7255 uno::Reference<container::XNameAccess> xTypeDetection(xSFactory->createInstance(u"com.sun.star.document.TypeDetection"_ustr), uno::UNO_QUERY); 7256 const uno::Sequence<OUString> aTypes = xTypeDetection->getElementNames(); 7257 tools::JsonWriter aJson; 7258 for (const OUString& rType : aTypes) 7259 { 7260 uno::Sequence<beans::PropertyValue> aValues; 7261 if (xTypeDetection->getByName(rType) >>= aValues) 7262 { 7263 auto it = std::find_if(std::cbegin(aValues), std::cend(aValues), [](const beans::PropertyValue& rValue) { return rValue.Name == "MediaType"; }); 7264 OUString aValue; 7265 if (it != std::cend(aValues) && (it->Value >>= aValue) && !aValue.isEmpty()) 7266 { 7267 auto typeNode = aJson.startNode(rType.toUtf8()); 7268 aJson.put("MediaType", aValue.toUtf8()); 7269 } 7270 } 7271 } 7272 7273 return convertOString(aJson.finishAndGetAsOString()); 7274 } 7275 7276 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long const features) 7277 { 7278 comphelper::ProfileZone aZone("lo_setOptionalFeatures"); 7279 7280 SolarMutexGuard aGuard; 7281 SetLastExceptionMsg(); 7282 7283 LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis); 7284 pLib->mOptionalFeatures = features; 7285 if (features & LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK) 7286 comphelper::LibreOfficeKit::setPartInInvalidation(true); 7287 if (features & LOK_FEATURE_NO_TILED_ANNOTATIONS) 7288 comphelper::LibreOfficeKit::setTiledAnnotations(false); 7289 if (features & LOK_FEATURE_RANGE_HEADERS) 7290 comphelper::LibreOfficeKit::setRangeHeaders(true); 7291 if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK) 7292 comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true); 7293 } 7294 7295 static void lo_setDocumentPassword(LibreOfficeKit* pThis, 7296 const char* pURL, const char* pPassword) 7297 { 7298 comphelper::ProfileZone aZone("lo_setDocumentPassword"); 7299 7300 SolarMutexGuard aGuard; 7301 SetLastExceptionMsg(); 7302 7303 assert(pThis); 7304 assert(pURL); 7305 LibLibreOffice_Impl *const pLib = static_cast<LibLibreOffice_Impl*>(pThis); 7306 assert(pLib->mInteractionMap.find(OString(pURL)) != pLib->mInteractionMap.end()); 7307 pLib->mInteractionMap.find(OString(pURL))->second->SetPassword(pPassword); 7308 } 7309 7310 static char* lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit* /*pThis*/) 7311 { 7312 SetLastExceptionMsg(); 7313 return convertOUString(ReplaceStringHookProc( 7314 u"{ " 7315 "\"ProductName\": \"%PRODUCTNAME\", " 7316 "\"ProductVersion\": \"%PRODUCTVERSION\", " 7317 "\"ProductExtension\": \"%PRODUCTEXTENSION\", " 7318 "\"BuildId\": \"%BUILDID\"" 7319 #if BUILDCONFIG_RECORDED 7320 ", \"BuildConfig\": \"" BUILDCONFIG "\"" 7321 #endif 7322 " }"_ustr)); 7323 } 7324 7325 static void aBasicErrorFunc(const OUString& rError, const OUString& rAction) 7326 { 7327 OString aBuffer = "Unexpected dialog: " + 7328 OUStringToOString(rAction, RTL_TEXTENCODING_ASCII_US) + 7329 " Error: " + 7330 OUStringToOString(rError, RTL_TEXTENCODING_ASCII_US); 7331 7332 fprintf(stderr, "Unexpected basic error dialog '%s'\n", aBuffer.getStr()); 7333 } 7334 7335 static bool initialize_uno(const OUString& aAppProgramURL) 7336 { 7337 #ifdef IOS 7338 // For iOS we already hardcode the inifile as "rc" in the .app directory. 7339 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("fundamental")); 7340 xContext = cppu::defaultBootstrap_InitialComponentContext(aAppProgramURL + "/rc"); 7341 #elif defined MACOSX 7342 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/../Resources/" SAL_CONFIGFILE("soffice")); 7343 xContext = cppu::defaultBootstrap_InitialComponentContext(); 7344 #else 7345 rtl::Bootstrap::setIniFilename(aAppProgramURL + "/" SAL_CONFIGFILE("soffice")); 7346 xContext = cppu::defaultBootstrap_InitialComponentContext(); 7347 #endif 7348 7349 if (!xContext.is()) 7350 { 7351 SetLastExceptionMsg(u"XComponentContext could not be created"_ustr); 7352 SAL_INFO("lok", "XComponentContext could not be created"); 7353 return false; 7354 } 7355 7356 xFactory = xContext->getServiceManager(); 7357 if (!xFactory.is()) 7358 { 7359 SetLastExceptionMsg(u"XMultiComponentFactory could not be created"_ustr); 7360 SAL_INFO("lok", "XMultiComponentFactory could not be created"); 7361 return false; 7362 } 7363 7364 xSFactory.set(xFactory, uno::UNO_QUERY_THROW); 7365 comphelper::setProcessServiceFactory(xSFactory); 7366 7367 SAL_INFO("lok", "Uno initialized - " << xContext.is()); 7368 7369 // set UserInstallation to user profile dir in test/user-template 7370 // rtl::Bootstrap aDefaultVars; 7371 // aDefaultVars.set(OUString("UserInstallation"), aAppProgramURL + "../registry" ); 7372 // configmgr setup ? 7373 7374 return true; 7375 } 7376 7377 // pre-unipoll version. 7378 static void lo_startmain(void*) 7379 { 7380 osl_setThreadName("lo_startmain"); 7381 7382 if (comphelper::SolarMutex::get()) 7383 Application::GetSolarMutex().tryToAcquire(); 7384 7385 Application::UpdateMainThread(); 7386 7387 soffice_main(); 7388 7389 Application::ReleaseSolarMutex(); 7390 } 7391 7392 // unipoll version. 7393 static void lo_runLoop(LibreOfficeKit* /*pThis*/, 7394 LibreOfficeKitPollCallback pPollCallback, 7395 LibreOfficeKitWakeCallback pWakeCallback, 7396 void* pData) 7397 { 7398 #if defined(IOS) || defined(ANDROID) || defined(__EMSCRIPTEN__) 7399 Application::GetSolarMutex().acquire(); 7400 #endif 7401 7402 { 7403 SolarMutexGuard aGuard; 7404 7405 vcl::lok::registerPollCallbacks(pPollCallback, pWakeCallback, pData); 7406 Application::UpdateMainThread(); 7407 soffice_main(); 7408 } 7409 #if defined(IOS) || defined(ANDROID) || defined(__EMSCRIPTEN__) 7410 vcl::lok::unregisterPollCallbacks(); 7411 Application::ReleaseSolarMutex(); 7412 #endif 7413 } 7414 7415 static bool bInitialized = false; 7416 7417 static void lo_status_indicator_callback(void *data, comphelper::LibreOfficeKit::statusIndicatorCallbackType type, int percent, const char* pText) 7418 { 7419 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(data); 7420 7421 if (!pLib->mpCallback) 7422 return; 7423 7424 switch (type) 7425 { 7426 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Start: 7427 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_START, pText, pLib->mpCallbackData); 7428 break; 7429 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::SetValue: 7430 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE, 7431 OUString(OUString::number(percent)).toUtf8().getStr(), pLib->mpCallbackData); 7432 break; 7433 case comphelper::LibreOfficeKit::statusIndicatorCallbackType::Finish: 7434 pLib->mpCallback(LOK_CALLBACK_STATUS_INDICATOR_FINISH, nullptr, pLib->mpCallbackData); 7435 break; 7436 } 7437 } 7438 7439 /// Used by preloadData (LibreOfficeKit) for providing different shortcuts for different languages. 7440 static void preLoadShortCutAccelerators() 7441 { 7442 std::unordered_map<OUString, css::uno::Reference<com::sun::star::ui::XAcceleratorConfiguration>>& acceleratorConfs = SfxLokHelper::getAcceleratorConfs(); 7443 css::uno::Sequence<OUString> installedLocales(officecfg::Setup::Office::InstalledLocales::get()->getElementNames()); 7444 OUString actualLang = officecfg::Setup::L10N::ooLocale::get(); 7445 7446 for (sal_Int32 i = 0; i < installedLocales.getLength(); i++) 7447 { 7448 // Set the UI language to current one, before creating the accelerator. 7449 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 7450 officecfg::Setup::L10N::ooLocale::set(installedLocales[i], batch); 7451 batch->commit(); 7452 7453 // Supported module names: Writer, Calc, Draw, Impress 7454 static constexpr OUString supportedModuleNames[] = { 7455 u"com.sun.star.text.TextDocument"_ustr, 7456 u"com.sun.star.sheet.SpreadsheetDocument"_ustr, 7457 u"com.sun.star.drawing.DrawingDocument"_ustr, 7458 u"com.sun.star.presentation.PresentationDocument"_ustr, 7459 }; 7460 // Create the accelerators. 7461 for (const OUString& supportedModuleName : supportedModuleNames) 7462 { 7463 OUString key = supportedModuleName + installedLocales[i]; 7464 acceleratorConfs[key] = svt::AcceleratorExecute::lok_createNewAcceleratorConfiguration(::comphelper::getProcessComponentContext(), supportedModuleName); 7465 } 7466 } 7467 7468 // Set the UI language back to default one. 7469 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 7470 officecfg::Setup::L10N::ooLocale::set(actualLang, batch); 7471 batch->commit(); 7472 } 7473 7474 void setLanguageToolConfig(); 7475 7476 /// Used only by LibreOfficeKit when used by Online to pre-initialize 7477 static void preloadData() 7478 { 7479 comphelper::ProfileZone aZone("preload data"); 7480 7481 // Create user profile in the temp directory for loading the dictionaries 7482 OUString sUserPath; 7483 rtl::Bootstrap::get(u"UserInstallation"_ustr, sUserPath); 7484 utl::TempFileNamed aTempDir(nullptr, true); 7485 aTempDir.EnableKillingFile(); 7486 rtl::Bootstrap::set(u"UserInstallation"_ustr, aTempDir.GetURL()); 7487 7488 // Register the bundled extensions 7489 desktop::Desktop::SynchronizeExtensionRepositories(true); 7490 bool bAbort = desktop::Desktop::CheckExtensionDependencies(); 7491 if(bAbort) 7492 std::cerr << "CheckExtensionDependencies failed" << std::endl; 7493 7494 std::cerr << "Preload textencodings"; // sal_textenc 7495 // Use RTL_TEXTENCODING_MS_1250 to trigger Impl_getTextEncodingData 7496 // to dlopen sal_textenclo 7497 (void)OUStringToOString(u"arbitrary string", RTL_TEXTENCODING_MS_1250); 7498 std::cerr << "\n"; 7499 7500 // setup LanguageTool config before spell checking init 7501 setLanguageToolConfig(); 7502 7503 // preload all available dictionaries 7504 linguistic2::DictionaryList::create(comphelper::getProcessComponentContext()); 7505 css::uno::Reference<css::linguistic2::XLinguServiceManager> xLngSvcMgr = 7506 css::linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext()); 7507 css::uno::Reference<linguistic2::XSpellChecker> xSpellChecker(xLngSvcMgr->getSpellChecker()); 7508 7509 std::cerr << "Preloading dictionaries: "; 7510 css::uno::Reference<linguistic2::XSupportedLocales> xSpellLocales(xSpellChecker, css::uno::UNO_QUERY_THROW); 7511 uno::Sequence< css::lang::Locale > aLocales = xSpellLocales->getLocales(); 7512 for (auto& it : aLocales) 7513 { 7514 std::cerr << LanguageTag::convertToBcp47(it) << " "; 7515 css::beans::PropertyValues aNone; 7516 xSpellChecker->isValid(u"forcefed"_ustr, it, aNone); 7517 } 7518 std::cerr << "\n"; 7519 7520 // Hack to load and cache the module liblocaledata_others.so which is not loaded normally 7521 // (when loading dictionaries of just non-Asian locales). Creating a XCalendar4 of one Asian locale 7522 // will cheaply load this missing "others" locale library. Appending an Asian locale in 7523 // LOK_ALLOWLIST_LANGUAGES env-var also works but at the cost of loading that dictionary. 7524 css::uno::Reference< css::i18n::XCalendar4 > xCal = css::i18n::LocaleCalendar2::create(comphelper::getProcessComponentContext()); 7525 css::lang::Locale aAsianLocale = { u"hi"_ustr, u"IN"_ustr, {} }; 7526 xCal->loadDefaultCalendar(aAsianLocale); 7527 7528 // preload all available thesauri 7529 css::uno::Reference<linguistic2::XThesaurus> xThesaurus(xLngSvcMgr->getThesaurus()); 7530 css::uno::Reference<linguistic2::XSupportedLocales> xThesLocales(xSpellChecker, css::uno::UNO_QUERY_THROW); 7531 aLocales = xThesLocales->getLocales(); 7532 std::cerr << "Preloading thesauri: "; 7533 for (auto& it : aLocales) 7534 { 7535 std::cerr << LanguageTag::convertToBcp47(it) << " "; 7536 css::beans::PropertyValues aNone; 7537 xThesaurus->queryMeanings(u"forcefed"_ustr, it, aNone); 7538 } 7539 std::cerr << "\n"; 7540 7541 css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create( 7542 comphelper::getProcessComponentContext()); 7543 xGlobalCfg->getAllKeyEvents(); 7544 7545 std::cerr << "Preload icons\n"; 7546 ImageTree &images = ImageTree::get(); 7547 images.getImageUrl(u"forcefed.png"_ustr, u"style"_ustr, u"FO_oo"_ustr); 7548 7549 std::cerr << "Preload short cut accelerators\n"; 7550 preLoadShortCutAccelerators(); 7551 7552 std::cerr << "Preload languages\n"; 7553 7554 // force load language singleton 7555 SvtLanguageTable::HasLanguageType(LANGUAGE_SYSTEM); 7556 (void)LanguageTag::isValidBcp47(u"foo"_ustr, nullptr); 7557 7558 std::cerr << "Preload fonts\n"; 7559 7560 // Initialize fonts. 7561 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext); 7562 if (xLangSrv.is()) 7563 { 7564 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker(); 7565 if (xSpell.is()) 7566 aLocales = xSpell->getLocales(); 7567 } 7568 7569 for (const auto& aLocale : aLocales) 7570 { 7571 //TODO: Add more types and cache more aggressively. For now this initializes the fontcache. 7572 using namespace ::com::sun::star::i18n::ScriptType; 7573 LanguageType nLang; 7574 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), LATIN); 7575 OutputDevice::GetDefaultFont(DefaultFontType::LATIN_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne); 7576 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), ASIAN); 7577 OutputDevice::GetDefaultFont(DefaultFontType::CJK_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne); 7578 nLang = MsLangId::resolveSystemLanguageByScriptType(LanguageTag::convertToLanguageType(aLocale, false), COMPLEX); 7579 OutputDevice::GetDefaultFont(DefaultFontType::CTL_SPREADSHEET, nLang, GetDefaultFontFlags::OnlyOne); 7580 } 7581 7582 std::cerr << "Preload config\n"; 7583 #if defined __GNUC__ || defined __clang__ 7584 #pragma GCC diagnostic push 7585 #pragma GCC diagnostic ignored "-Wunused-variable" 7586 #endif 7587 static SvtOptionsDialogOptions aDialogOptions; 7588 static SvtCTLOptions aSvtCTLOptions; 7589 static svtools::ColorConfig aColorConfig; 7590 static SvtMiscOptions aSvtMiscOptions; 7591 static SvtCommandOptions aSvtCommandOptions; 7592 static SvtLinguConfig aSvtLinguConfig; 7593 static SvtModuleOptions aSvtModuleOptions; 7594 static SvtPathOptions aSvtPathOptions; 7595 static SvtSearchOptions aSvtSearchOptions; 7596 static SvtSysLocaleOptions aSvtSysLocaleOptions; 7597 static SvtUserOptions aSvtUserOptions; 7598 //static SvtViewOptions aSvtViewOptions; 7599 static MouseSettings aMouseSettings; 7600 static StyleSettings aStyleSettings; 7601 static MiscSettings aMiscSettings; 7602 static HelpSettings aHelpSettings; 7603 static AllSettings aAllSettings; 7604 #if defined __GNUC__ || defined __clang__ 7605 #pragma GCC diagnostic pop 7606 #endif 7607 7608 static constexpr OUString preloadComponents[] = { 7609 u"private:factory/swriter"_ustr, 7610 u"private:factory/scalc"_ustr, 7611 u"private:factory/simpress"_ustr, 7612 u"private:factory/sdraw"_ustr 7613 }; 7614 // getting the remote LibreOffice service manager 7615 uno::Reference<frame::XDesktop2> xCompLoader(frame::Desktop::create(xContext)); 7616 7617 // Preload and close each of the main components once to initialize global state 7618 uno::Sequence<css::beans::PropertyValue> szEmptyArgs(0); 7619 for (const auto& component : preloadComponents) 7620 { 7621 auto xComp = xCompLoader->loadComponentFromURL(component, "_blank", 0, szEmptyArgs); 7622 xComp->dispose(); 7623 } 7624 7625 // Set user profile's path back to the original one 7626 rtl::Bootstrap::set(u"UserInstallation"_ustr, sUserPath); 7627 } 7628 7629 namespace { 7630 7631 static void activateNotebookbar(std::u16string_view rApp) 7632 { 7633 OUString aPath = OUString::Concat("org.openoffice.Office.UI.ToolbarMode/Applications/") + rApp; 7634 7635 const utl::OConfigurationTreeRoot aAppNode(xContext, aPath, true); 7636 7637 if (aAppNode.isValid()) 7638 { 7639 static constexpr OUString sNoteBookbarName(u"notebookbar_online.ui"_ustr); 7640 aAppNode.setNodeValue(u"Active"_ustr, Any(sNoteBookbarName)); 7641 7642 const utl::OConfigurationNode aImplsNode = aAppNode.openNode(u"Modes"_ustr); 7643 const Sequence<OUString> aModeNodeNames( aImplsNode.getNodeNames() ); 7644 7645 for (const auto& rModeNodeName : aModeNodeNames) 7646 { 7647 const utl::OConfigurationNode aImplNode(aImplsNode.openNode(rModeNodeName)); 7648 if (!aImplNode.isValid()) 7649 continue; 7650 7651 OUString aCommandArg = comphelper::getString(aImplNode.getNodeValue(u"CommandArg"_ustr)); 7652 if (aCommandArg == "notebookbar.ui") 7653 aImplNode.setNodeValue(u"CommandArg"_ustr, Any(sNoteBookbarName)); 7654 } 7655 7656 aAppNode.commit(); 7657 } 7658 } 7659 7660 void setHelpRootURL() 7661 { 7662 const char* pHelpRootURL = ::getenv("LOK_HELP_URL"); 7663 if (pHelpRootURL) 7664 { 7665 OUString aHelpRootURL = OStringToOUString(pHelpRootURL, RTL_TEXTENCODING_UTF8); 7666 try 7667 { 7668 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 7669 officecfg::Office::Common::Help::HelpRootURL::set(aHelpRootURL, batch); 7670 batch->commit(); 7671 } 7672 catch (uno::Exception const& rException) 7673 { 7674 SAL_WARN("lok", "Failed to set the help root URL: " << rException.Message); 7675 } 7676 } 7677 } 7678 7679 void setCertificateDir() 7680 { 7681 const char* pEnvVarString = ::getenv("LO_CERTIFICATE_DATABASE_PATH"); 7682 if (pEnvVarString) 7683 { 7684 OUString aCertificateDatabasePath = OStringToOUString(pEnvVarString, RTL_TEXTENCODING_UTF8); 7685 try 7686 { 7687 std::shared_ptr<comphelper::ConfigurationChanges> pBatch(comphelper::ConfigurationChanges::create()); 7688 officecfg::Office::Common::Security::Scripting::CertDir::set(aCertificateDatabasePath, pBatch); 7689 officecfg::Office::Common::Security::Scripting::ManualCertDir::set(aCertificateDatabasePath, pBatch); 7690 pBatch->commit(); 7691 } 7692 catch (uno::Exception const& rException) 7693 { 7694 SAL_WARN("lok", "Failed to set the NSS certificate database directory: " << rException.Message); 7695 } 7696 } 7697 } 7698 7699 void setDeeplConfig() 7700 { 7701 const char* pAPIUrlString = ::getenv("DEEPL_API_URL"); 7702 const char* pAuthKeyString = ::getenv("DEEPL_AUTH_KEY"); 7703 if (pAPIUrlString && pAuthKeyString) 7704 { 7705 OUString aAPIUrl = OStringToOUString(pAPIUrlString, RTL_TEXTENCODING_UTF8); 7706 OUString aAuthKey = OStringToOUString(pAuthKeyString, RTL_TEXTENCODING_UTF8); 7707 try 7708 { 7709 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 7710 officecfg::Office::Linguistic::Translation::Deepl::ApiURL::set(aAPIUrl, batch); 7711 officecfg::Office::Linguistic::Translation::Deepl::AuthKey::set(aAuthKey, batch); 7712 batch->commit(); 7713 } 7714 catch(uno::Exception const& rException) 7715 { 7716 SAL_WARN("lok", "Failed to set Deepl API settings: " << rException.Message); 7717 } 7718 } 7719 } 7720 7721 void setLanguageToolConfig() 7722 { 7723 const char* pEnabled = ::getenv("LANGUAGETOOL_ENABLED"); 7724 const char* pBaseUrlString = ::getenv("LANGUAGETOOL_BASEURL"); 7725 7726 if (pEnabled && pBaseUrlString) 7727 { 7728 const char* pUsername = ::getenv("LANGUAGETOOL_USERNAME"); 7729 const char* pApikey = ::getenv("LANGUAGETOOL_APIKEY"); 7730 const char* pSSLVerification = ::getenv("LANGUAGETOOL_SSL_VERIFICATION"); 7731 const char* pRestProtocol = ::getenv("LANGUAGETOOL_RESTPROTOCOL"); 7732 7733 OUString aEnabled = OStringToOUString(pEnabled, RTL_TEXTENCODING_UTF8); 7734 if (aEnabled != "true") 7735 return; 7736 OUString aBaseUrl = OStringToOUString(pBaseUrlString, RTL_TEXTENCODING_UTF8); 7737 try 7738 { 7739 using LanguageToolCfg = officecfg::Office::Linguistic::GrammarChecking::LanguageTool; 7740 auto batch(comphelper::ConfigurationChanges::create()); 7741 7742 LanguageToolCfg::BaseURL::set(aBaseUrl, batch); 7743 LanguageToolCfg::IsEnabled::set(true, batch); 7744 if (pSSLVerification) 7745 { 7746 OUString aSSLVerification = OStringToOUString(pSSLVerification, RTL_TEXTENCODING_UTF8); 7747 LanguageToolCfg::SSLCertVerify::set(aSSLVerification == "true", batch); 7748 } 7749 if (pRestProtocol) 7750 { 7751 OUString aRestProtocol = OStringToOUString(pRestProtocol, RTL_TEXTENCODING_UTF8); 7752 LanguageToolCfg::RestProtocol::set(aRestProtocol, batch); 7753 } 7754 if (pUsername && pApikey) 7755 { 7756 OUString aUsername = OStringToOUString(pUsername, RTL_TEXTENCODING_UTF8); 7757 OUString aApiKey = OStringToOUString(pApikey, RTL_TEXTENCODING_UTF8); 7758 LanguageToolCfg::Username::set(aUsername, batch); 7759 LanguageToolCfg::ApiKey::set(aApiKey, batch); 7760 } 7761 batch->commit(); 7762 7763 css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = 7764 css::linguistic2::LinguServiceManager::create(xContext); 7765 if (xLangSrv.is()) 7766 { 7767 css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker(); 7768 if (xSpell.is()) 7769 { 7770 Sequence<OUString> aEmpty; 7771 Sequence<css::lang::Locale> aLocales = xSpell->getLocales(); 7772 7773 uno::Reference<linguistic2::XProofreader> xGC( 7774 xContext->getServiceManager()->createInstanceWithContext(u"org.openoffice.lingu.LanguageToolGrammarChecker"_ustr, xContext), 7775 uno::UNO_QUERY_THROW); 7776 uno::Reference<linguistic2::XSupportedLocales> xSuppLoc(xGC, uno::UNO_QUERY_THROW); 7777 7778 for (int itLocale = 0; itLocale < aLocales.getLength(); itLocale++) 7779 { 7780 // turn off spell checker if LanguageTool supports the locale already 7781 if (xSuppLoc->hasLocale(aLocales[itLocale])) 7782 xLangSrv->setConfiguredServices( 7783 SN_SPELLCHECKER, aLocales[itLocale], aEmpty); 7784 } 7785 } 7786 } 7787 } 7788 catch(uno::Exception const& rException) 7789 { 7790 SAL_WARN("lok", "Failed to set LanguageTool API settings: " << rException.Message); 7791 } 7792 } 7793 } 7794 7795 } 7796 7797 static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char* pUserProfileUrl) 7798 { 7799 enum { 7800 PRE_INIT, // setup shared data in master process 7801 SECOND_INIT, // complete init. after fork 7802 FULL_INIT // do a standard complete init. 7803 } eStage; 7804 7805 // Did we do a pre-initialize 7806 static bool bPreInited = false; 7807 static bool bUnipoll = false; 7808 static bool bProfileZones = false; 7809 static bool bNotebookbar = false; 7810 7811 { // cf. string lifetime for preinit 7812 std::vector<OUString> aOpts; 7813 7814 // ':' delimited options - avoiding ABI change for new parameters 7815 const char *pOptions = getenv("SAL_LOK_OPTIONS"); 7816 if (pOptions) 7817 aOpts = comphelper::string::split(OUString(pOptions, strlen(pOptions), RTL_TEXTENCODING_UTF8), ':'); 7818 for (const auto &it : aOpts) 7819 { 7820 if (it == "unipoll") 7821 bUnipoll = true; 7822 else if (it == "profile_events") 7823 bProfileZones = true; 7824 else if (it == "sc_no_grid_bg") 7825 comphelper::LibreOfficeKit::setCompatFlag( 7826 comphelper::LibreOfficeKit::Compat::scNoGridBackground); 7827 else if (it == "sc_print_twips_msgs") 7828 comphelper::LibreOfficeKit::setCompatFlag( 7829 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs); 7830 else if (it == "notebookbar") 7831 bNotebookbar = true; 7832 } 7833 } 7834 7835 char* pAllowlist = ::getenv("LOK_HOST_ALLOWLIST"); 7836 if (pAllowlist) 7837 { 7838 HostFilter::setAllowedHostsRegex(pAllowlist); 7839 } 7840 7841 // What stage are we at ? 7842 if (pThis == nullptr) 7843 { 7844 eStage = PRE_INIT; 7845 if (lok_preinit_2_called) 7846 { 7847 SAL_INFO("lok", "Create libreoffice object"); 7848 gImpl = new LibLibreOffice_Impl(); 7849 } 7850 } 7851 else if (bPreInited) 7852 eStage = SECOND_INIT; 7853 else 7854 eStage = FULL_INIT; 7855 7856 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 7857 7858 if (bInitialized) 7859 return 1; 7860 7861 // Turn profile zones on early 7862 if (bProfileZones && eStage == SECOND_INIT) 7863 { 7864 comphelper::TraceEvent::startRecording(); 7865 traceEventDumper = new TraceEventDumper(); 7866 } 7867 7868 comphelper::ProfileZone aZone("lok-init"); 7869 7870 if (eStage == PRE_INIT) 7871 { 7872 rtl_alloc_preInit(true); 7873 7874 // Set the default timezone to the TZ envar, if set. 7875 const char* tz = ::getenv("TZ"); 7876 SfxLokHelper::setDefaultTimezone(!!tz, tz ? OStringToOUString(tz, RTL_TEXTENCODING_UTF8) 7877 : OUString()); 7878 } 7879 7880 if (eStage != SECOND_INIT) 7881 comphelper::LibreOfficeKit::setActive(); 7882 7883 if (eStage != PRE_INIT) 7884 comphelper::LibreOfficeKit::setStatusIndicatorCallback(lo_status_indicator_callback, pLib); 7885 7886 if (pUserProfileUrl && eStage != PRE_INIT) 7887 { 7888 OUString url( 7889 pUserProfileUrl, strlen(pUserProfileUrl), RTL_TEXTENCODING_UTF8); 7890 OUString path; 7891 if (url.startsWithIgnoreAsciiCase("vnd.sun.star.pathname:", &path)) 7892 { 7893 OUString url2; 7894 osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath( 7895 path, url2); 7896 if (e == osl::FileBase::E_None) 7897 url = url2; 7898 else 7899 SAL_WARN("lok", "resolving <" << url << "> failed with " << +e); 7900 } 7901 rtl::Bootstrap::set(u"UserInstallation"_ustr, url); 7902 if (eStage == SECOND_INIT) 7903 utl::Bootstrap::reloadData(); 7904 } 7905 7906 OUString aAppPath; 7907 if (pAppPath) 7908 { 7909 aAppPath = OUString(pAppPath, strlen(pAppPath), RTL_TEXTENCODING_UTF8); 7910 } 7911 else 7912 { 7913 #if defined ANDROID || defined EMSCRIPTEN 7914 aAppPath = OUString::fromUtf8(lo_get_app_data_dir()) + "/program"; 7915 #else 7916 // Fun conversion dance back and forth between URLs and system paths... 7917 OUString aAppURL; 7918 ::osl::Module::getUrlFromAddress( reinterpret_cast< oslGenericFunction >(lo_initialize), 7919 aAppURL); 7920 osl::FileBase::getSystemPathFromFileURL( aAppURL, aAppPath ); 7921 #endif 7922 7923 #ifdef IOS 7924 // The above gives something like 7925 // "/private/var/containers/Bundle/Application/953AA851-CC15-4C60-A2CB-C2C6F24E6F71/Foo.app/Foo", 7926 // and we want to drop the final component (the binary name). 7927 sal_Int32 lastSlash = aAppPath.lastIndexOf('/'); 7928 assert(lastSlash > 0); 7929 aAppPath = aAppPath.copy(0, lastSlash); 7930 #endif 7931 } 7932 7933 OUString aAppURL; 7934 if (osl::FileBase::getFileURLFromSystemPath(aAppPath, aAppURL) != osl::FileBase::E_None) 7935 return 0; 7936 7937 #ifdef IOS 7938 // A LibreOffice-using iOS app should have the ICU data file in the app bundle. Initialize ICU 7939 // to use that. 7940 NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; 7941 7942 int fd = open([[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String], O_RDONLY); 7943 if (fd == -1) 7944 NSLog(@"Could not open ICU data file %s", [[bundlePath stringByAppendingPathComponent:@"ICU.dat"] UTF8String]); 7945 else 7946 { 7947 struct stat st; 7948 if (fstat(fd, &st) == -1) 7949 NSLog(@"fstat on ICU data file failed: %s", strerror(errno)); 7950 else 7951 { 7952 void *icudata = mmap(0, (size_t) st.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0); 7953 if (icudata == MAP_FAILED) 7954 NSLog(@"mmap failed: %s", strerror(errno)); 7955 else 7956 { 7957 UErrorCode icuStatus = U_ZERO_ERROR; 7958 udata_setCommonData(icudata, &icuStatus); 7959 if (U_FAILURE(icuStatus)) 7960 NSLog(@"udata_setCommonData failed"); 7961 else 7962 { 7963 // Quick test that ICU works... 7964 UConverter *cnv = ucnv_open("iso-8859-3", &icuStatus); 7965 if (U_SUCCESS(icuStatus)) 7966 ucnv_close(cnv); 7967 else 7968 NSLog(@"ucnv_open() failed: %s", u_errorName(icuStatus)); 7969 } 7970 } 7971 } 7972 close(fd); 7973 } 7974 #endif 7975 7976 try 7977 { 7978 if (eStage != SECOND_INIT) 7979 { 7980 SAL_INFO("lok", "Attempting to initialize UNO"); 7981 7982 if (!initialize_uno(aAppURL)) 7983 return false; 7984 7985 // Force headless -- this is only for bitmap rendering. 7986 rtl::Bootstrap::set(u"SAL_USE_VCLPLUGIN"_ustr, u"svp"_ustr); 7987 7988 // We specifically need to make sure we have the "headless" 7989 // command arg set (various code specifically checks via 7990 // CommandLineArgs): 7991 desktop::Desktop::GetCommandLineArgs().setHeadless(); 7992 7993 #ifdef IOS 7994 if (InitVCL() && [NSThread isMainThread]) 7995 { 7996 static bool bFirstTime = true; 7997 if (bFirstTime) 7998 { 7999 Application::GetSolarMutex().release(); 8000 bFirstTime = false; 8001 } 8002 } 8003 SfxApplication::GetOrCreate(); 8004 #endif 8005 8006 #if HAVE_FEATURE_ANDROID_LOK 8007 // Register the bundled extensions - so that the dictionaries work 8008 desktop::Desktop::SynchronizeExtensionRepositories(false); 8009 bool bFailed = desktop::Desktop::CheckExtensionDependencies(); 8010 if (bFailed) 8011 SAL_INFO("lok", "CheckExtensionDependencies failed"); 8012 #endif 8013 8014 if (eStage == PRE_INIT) 8015 { 8016 { 8017 comphelper::ProfileZone aInit("Init vcl"); 8018 std::cerr << "Init vcl\n"; 8019 InitVCL(); 8020 } 8021 8022 // pre-load all component libraries. 8023 if (!xContext.is()) 8024 throw css::uno::DeploymentException(u"preInit: XComponentContext is not created"_ustr); 8025 8026 css::uno::Reference< css::uno::XInterface > xService; 8027 xContext->getValueByName(u"/singletons/com.sun.star.lang.theServiceManager"_ustr) >>= xService; 8028 if (!xService.is()) 8029 throw css::uno::DeploymentException(u"preInit: XMultiComponentFactory is not created"_ustr); 8030 8031 css::uno::Reference<css::lang::XInitialization> aService( 8032 xService, css::uno::UNO_QUERY_THROW); 8033 8034 // pre-requisites: 8035 // In order to load implementations and invoke 8036 // component factory it is required: 8037 // 1) defaultBootstrap_InitialComponentContext() 8038 // 2) comphelper::setProcessServiceFactory(xSFactory); 8039 // 3) InitVCL() 8040 { 8041 comphelper::ProfileZone aInit("preload"); 8042 aService->initialize({css::uno::Any(u"preload"_ustr)}); 8043 } 8044 { // Force load some modules 8045 comphelper::ProfileZone aInit("preload modules"); 8046 VclBuilderPreload(); 8047 VclAbstractDialogFactory::Create(); 8048 } 8049 8050 preloadData(); 8051 8052 // Release Solar Mutex, lo_startmain thread should acquire it. 8053 Application::ReleaseSolarMutex(); 8054 } 8055 8056 setLanguageAndLocale(u"en-US"_ustr); 8057 } 8058 8059 if (eStage != PRE_INIT) 8060 { 8061 SAL_INFO("lok", "Re-initialize temp paths"); 8062 SvtPathOptions aOptions; 8063 OUString aNewTemp; 8064 osl::FileBase::getTempDirURL(aNewTemp); 8065 aOptions.SetTempPath(aNewTemp); 8066 { 8067 const char *pWorkPath = getenv("LOK_WORKDIR"); 8068 if (pWorkPath) 8069 { 8070 OString sWorkPath(pWorkPath); 8071 aOptions.SetWorkPath(OStringToOUString(sWorkPath, RTL_TEXTENCODING_UTF8)); 8072 } 8073 } 8074 desktop::Desktop::CreateTemporaryDirectory(); 8075 8076 // The RequestHandler is specifically set to be ready when all the other 8077 // init in Desktop::Main (run from soffice_main) is done. We can enable 8078 // the RequestHandler here (without starting any IPC thread; 8079 // shortcutting the invocation in Desktop::Main that would start the IPC 8080 // thread), and can then use it to wait until we're definitely ready to 8081 // continue. 8082 8083 SAL_INFO("lok", "Enabling RequestHandler"); 8084 RequestHandler::Enable(false); 8085 SAL_INFO("lok", "Starting soffice_main"); 8086 RequestHandler::SetReady(false); 8087 if (!bUnipoll) 8088 { 8089 // Start the main thread only in non-unipoll mode (i.e. multithreaded). 8090 pLib->maThread = osl_createThread(lo_startmain, nullptr); 8091 SAL_INFO("lok", "Waiting for RequestHandler"); 8092 RequestHandler::WaitForReady(); 8093 SAL_INFO("lok", "RequestHandler ready -- continuing"); 8094 } 8095 else 8096 InitVCL(); 8097 } 8098 8099 if (eStage != SECOND_INIT) 8100 ErrorRegistry::RegisterDisplay(aBasicErrorFunc); 8101 8102 SAL_INFO("lok", "LOK Initialized"); 8103 if (eStage == PRE_INIT) 8104 bPreInited = true; 8105 else 8106 bInitialized = true; 8107 } 8108 catch (css::uno::Exception& exception) 8109 { 8110 fprintf(stderr, "Bootstrapping exception '%s'\n", 8111 OUStringToOString(exception.Message, RTL_TEXTENCODING_UTF8).getStr()); 8112 } 8113 8114 if (eStage == PRE_INIT) 8115 { 8116 comphelper::ThreadPool::getSharedOptimalPool().shutdown(); 8117 } 8118 8119 // Turn off quick editing on iOS, Android and Emscripten 8120 #if defined IOS || defined ANDROID || defined __EMSCRIPTEN__ 8121 if (officecfg::Office::Impress::Misc::TextObject::QuickEditing::get()) 8122 { 8123 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 8124 officecfg::Office::Impress::Misc::TextObject::QuickEditing::set(false, batch); 8125 batch->commit(); 8126 } 8127 #endif 8128 8129 8130 setHelpRootURL(); 8131 setCertificateDir(); 8132 setDeeplConfig(); 8133 setLanguageToolConfig(); 8134 8135 if (bNotebookbar) 8136 { 8137 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 8138 officecfg::Office::UI::ToolbarMode::ActiveWriter::set(u"notebookbar_online.ui"_ustr, batch); 8139 officecfg::Office::UI::ToolbarMode::ActiveCalc::set(u"notebookbar_online.ui"_ustr, batch); 8140 officecfg::Office::UI::ToolbarMode::ActiveImpress::set(u"notebookbar_online.ui"_ustr, batch); 8141 officecfg::Office::UI::ToolbarMode::ActiveDraw::set(u"notebookbar_online.ui"_ustr, batch); 8142 batch->commit(); 8143 8144 activateNotebookbar(u"Writer"); 8145 activateNotebookbar(u"Calc"); 8146 activateNotebookbar(u"Impress"); 8147 activateNotebookbar(u"Draw"); 8148 } 8149 8150 // staticize all strings. 8151 if (eStage == PRE_INIT) 8152 rtl_alloc_preInit(false); 8153 8154 return bInitialized; 8155 } 8156 8157 SAL_JNI_EXPORT 8158 LibreOfficeKit *libreofficekit_hook_2(const char* install_path, const char* user_profile_url) 8159 { 8160 static bool alreadyCalled = false; 8161 8162 if ((!lok_preinit_2_called && !gImpl) || (lok_preinit_2_called && !alreadyCalled)) 8163 { 8164 alreadyCalled = true; 8165 8166 if (!lok_preinit_2_called) 8167 { 8168 SAL_INFO("lok", "Create libreoffice object"); 8169 gImpl = new LibLibreOffice_Impl(); 8170 } 8171 8172 if (!lo_initialize(gImpl, install_path, user_profile_url)) 8173 { 8174 lo_destroy(gImpl); 8175 } 8176 } 8177 return static_cast<LibreOfficeKit*>(gImpl); 8178 } 8179 8180 SAL_JNI_EXPORT 8181 LibreOfficeKit *libreofficekit_hook(const char* install_path) 8182 { 8183 return libreofficekit_hook_2(install_path, nullptr); 8184 } 8185 8186 SAL_JNI_EXPORT 8187 int lok_preinit(const char* install_path, const char* user_profile_url) 8188 { 8189 return lo_initialize(nullptr, install_path, user_profile_url); 8190 } 8191 8192 SAL_JNI_EXPORT 8193 int lok_preinit_2(const char* install_path, const char* user_profile_url, LibreOfficeKit** kit) 8194 { 8195 lok_preinit_2_called = true; 8196 int result = lo_initialize(nullptr, install_path, user_profile_url); 8197 if (kit != nullptr) 8198 *kit = gImpl; 8199 return result; 8200 } 8201 8202 static void lo_destroy(LibreOfficeKit* pThis) 8203 { 8204 SolarMutexClearableGuard aGuard; 8205 8206 LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis); 8207 gImpl = nullptr; 8208 8209 SAL_INFO("lok", "LO Destroy"); 8210 8211 comphelper::LibreOfficeKit::setStatusIndicatorCallback(nullptr, nullptr); 8212 uno::Reference <frame::XDesktop2> xDesktop = frame::Desktop::create ( ::comphelper::getProcessComponentContext() ); 8213 // FIXME: the terminate() call here is a no-op because it detects 8214 // that LibreOfficeKit::isActive() and then returns early! 8215 bool bSuccess = xDesktop.is() && xDesktop->terminate(); 8216 8217 if (!bSuccess) 8218 { 8219 bSuccess = GetpApp() && GetpApp()->QueryExit(); 8220 } 8221 8222 if (!bSuccess) 8223 { 8224 Application::Quit(); 8225 } 8226 8227 aGuard.clear(); 8228 8229 osl_joinWithThread(pLib->maThread); 8230 osl_destroyThread(pLib->maThread); 8231 8232 delete pLib; 8233 bInitialized = false; 8234 SAL_INFO("lok", "LO Destroy Done"); 8235 } 8236 8237 } // extern "C" 8238 8239 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 8240
