LibreOffice Module desktop (master)  1
init.cxx
Go to the documentation of this file.
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 <config_features.h>
11 
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #ifdef IOS
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <unicode/udata.h>
20 #include <unicode/ucnv.h>
21 #include <premac.h>
22 #import <Foundation/Foundation.h>
23 #import <CoreGraphics/CoreGraphics.h>
24 #include <postmac.h>
25 #endif
26 
27 #ifdef ANDROID
28 #include <osl/detail/android-bootstrap.h>
29 #endif
30 
31 #include <algorithm>
32 #include <memory>
33 #include <iostream>
34 #include <string_view>
35 
36 #include <boost/property_tree/json_parser.hpp>
37 #include <boost/algorithm/string.hpp>
38 
39 #include <LibreOfficeKit/LibreOfficeKit.h>
40 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
41 
42 #include <sal/log.hxx>
43 #include <vcl/errinf.hxx>
44 #include <vcl/lok.hxx>
45 #include <o3tl/any.hxx>
46 #include <osl/file.hxx>
47 #include <osl/process.h>
48 #include <osl/thread.h>
49 #include <rtl/bootstrap.hxx>
50 #include <rtl/strbuf.hxx>
51 #include <rtl/uri.hxx>
52 #include <svl/zforlist.hxx>
53 #include <cppuhelper/bootstrap.hxx>
54 #include <comphelper/base64.hxx>
56 #include <comphelper/lok.hxx>
58 #include <comphelper/string.hxx>
64 
65 #include <com/sun/star/document/MacroExecMode.hpp>
66 #include <com/sun/star/beans/XPropertySet.hpp>
67 #include <com/sun/star/container/XNameAccess.hpp>
68 #include <com/sun/star/frame/Desktop.hpp>
69 #include <com/sun/star/frame/DispatchResultEvent.hpp>
70 #include <com/sun/star/frame/DispatchResultState.hpp>
71 #include <com/sun/star/frame/XDispatchProvider.hpp>
72 #include <com/sun/star/frame/XDispatchResultListener.hpp>
73 #include <com/sun/star/frame/XSynchronousDispatch.hpp>
74 #include <com/sun/star/frame/XStorable.hpp>
75 #include <com/sun/star/lang/Locale.hpp>
76 #include <com/sun/star/lang/XComponent.hpp>
77 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
78 #include <com/sun/star/reflection/theCoreReflection.hpp>
79 #include <com/sun/star/reflection/XIdlClass.hpp>
80 #include <com/sun/star/reflection/XIdlReflection.hpp>
81 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
82 #include <com/sun/star/util/URLTransformer.hpp>
83 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
84 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
85 #include <com/sun/star/datatransfer/XTransferable2.hpp>
86 #include <com/sun/star/text/TextContentAnchorType.hpp>
87 #include <com/sun/star/document/XRedlinesSupplier.hpp>
88 #include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
89 
90 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
91 #include <com/sun/star/xml/crypto/XSEInitializer.hpp>
92 #include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
93 #include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
94 #include <com/sun/star/security/XCertificate.hpp>
95 
96 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
97 #include <com/sun/star/linguistic2/XSpellChecker.hpp>
98 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
99 #include <com/sun/star/i18n/ScriptType.hpp>
100 #include <com/sun/star/lang/DisposedException.hpp>
101 
102 #include <editeng/flstitem.hxx>
103 #ifdef IOS
104 #include <sfx2/app.hxx>
105 #endif
106 #include <sfx2/objsh.hxx>
107 #include <sfx2/docfilt.hxx>
108 #include <sfx2/docfile.hxx>
109 #include <sfx2/viewsh.hxx>
110 #include <sfx2/viewfrm.hxx>
111 #include <sfx2/msgpool.hxx>
112 #include <sfx2/dispatch.hxx>
113 #include <sfx2/lokcharthelper.hxx>
114 #include <sfx2/DocumentSigner.hxx>
117 #include <svx/dialmgr.hxx>
118 #include <svx/strings.hrc>
119 #include <svx/svdview.hxx>
120 #include <svx/svxids.hrc>
121 #include <svx/ucsubset.hxx>
122 #include <vcl/vclevent.hxx>
123 #include <vcl/GestureEvent.hxx>
124 #include <vcl/svapp.hxx>
125 #include <unotools/resmgr.hxx>
126 #include <tools/fract.hxx>
127 #include <tools/json_writer.hxx>
128 #include <svtools/ctrltool.hxx>
129 #include <svtools/langtab.hxx>
130 #include <vcl/fontcharmap.hxx>
131 #include <vcl/graphicfilter.hxx>
132 #ifdef IOS
133 #include <vcl/sysdata.hxx>
134 #endif
135 #include <vcl/virdev.hxx>
136 #include <vcl/ImageTree.hxx>
137 #include <vcl/ITiledRenderable.hxx>
138 #include <vcl/dialoghelper.hxx>
139 #include <unicode/uchar.h>
141 #include <unotools/configmgr.hxx>
142 #include <unotools/confignode.hxx>
145 #include <unotools/pathoptions.hxx>
146 #include <unotools/tempfile.hxx>
147 #include <unotools/streamwrap.hxx>
148 #include <osl/module.hxx>
149 #include <comphelper/sequence.hxx>
150 #include <sfx2/sfxbasemodel.hxx>
151 #include <svl/undo.hxx>
152 #include <unotools/datetime.hxx>
153 #include <i18nlangtag/mslangid.hxx>
155 #include <vcl/abstdlg.hxx>
156 #include <tools/diagnose_ex.h>
157 #include <vcl/uitest/uiobject.hxx>
158 #include <vcl/jsdialog/executor.hxx>
159 
160 // Needed for getUndoManager()
161 #include <com/sun/star/document/XUndoManager.hpp>
162 #include <com/sun/star/document/XUndoManagerSupplier.hpp>
163 #include <editeng/sizeitem.hxx>
164 #include <svx/rulritem.hxx>
165 #include <svx/pageitem.hxx>
166 
167 #include <app.hxx>
168 
169 #include "../app/cmdlineargs.hxx"
170 // We also need to hackily be able to start the main libreoffice thread:
171 #include "../app/sofficemain.h"
172 #include "../app/officeipcthread.hxx"
173 #include <lib/init.hxx>
174 
175 #include "lokinteractionhandler.hxx"
176 #include "lokclipboard.hxx"
177 #include <officecfg/Office/Impress.hxx>
178 
179 using namespace css;
180 using namespace vcl;
181 using namespace desktop;
182 using namespace utl;
183 
184 static LibLibreOffice_Impl *gImpl = nullptr;
185 static std::weak_ptr< LibreOfficeKitClass > gOfficeClass;
186 static std::weak_ptr< LibreOfficeKitDocumentClass > gDocumentClass;
187 
188 static void SetLastExceptionMsg(const OUString& s = OUString())
189 {
190  SAL_WARN_IF(!s.isEmpty(), "lok", "lok exception '" + s + "'");
191  if (gImpl)
192  gImpl->maLastExceptionMsg = s;
193 }
194 
195 namespace {
196 
197 struct ExtensionMap
198 {
199  const char *extn;
200  const char *filterName;
201 };
202 
203 class TraceEventDumper : public AutoTimer
204 {
205  static const int dumpTimeoutMS = 5000;
206 
207 public:
208  TraceEventDumper() : AutoTimer( "Trace Event dumper" )
209  {
210  SetTimeout(dumpTimeoutMS);
211  Start();
212  }
213  virtual void Invoke() override
214  {
215  const css::uno::Sequence<OUString> aEvents =
217  OStringBuffer aOutput;
218  for (const auto &s : aEvents)
219  {
220  aOutput.append(OUStringToOString(s, RTL_TEXTENCODING_UTF8));
221  aOutput.append("\n");
222  }
223  if (aOutput.getLength() > 0)
224  {
225  OString aChunk = aOutput.makeStringAndClear();
226  if (gImpl && gImpl->mpCallback)
227  gImpl->mpCallback(LOK_CALLBACK_PROFILE_FRAME, aChunk.getStr(), gImpl->mpCallbackData);
228  }
229  }
230 };
231 
232 } // unnamed namespace
233 
234 static TraceEventDumper *traceEventDumper = nullptr;
235 
236 const ExtensionMap aWriterExtensionMap[] =
237 {
238  { "doc", "MS Word 97" },
239  { "docm", "MS Word 2007 XML VBA" },
240  { "docx", "MS Word 2007 XML" },
241  { "fodt", "OpenDocument Text Flat XML" },
242  { "html", "HTML (StarWriter)" },
243  { "odt", "writer8" },
244  { "ott", "writer8_template" },
245  { "pdf", "writer_pdf_Export" },
246  { "epub", "EPUB" },
247  { "rtf", "Rich Text Format" },
248  { "txt", "Text" },
249  { "xhtml", "XHTML Writer File" },
250  { "png", "writer_png_Export" },
251  { nullptr, nullptr }
252 };
253 
254 const ExtensionMap aCalcExtensionMap[] =
255 {
256  { "csv", "Text - txt - csv (StarCalc)" },
257  { "fods", "OpenDocument Spreadsheet Flat XML" },
258  { "html", "HTML (StarCalc)" },
259  { "ods", "calc8" },
260  { "ots", "calc8_template" },
261  { "pdf", "calc_pdf_Export" },
262  { "xhtml", "XHTML Calc File" },
263  { "xls", "MS Excel 97" },
264  { "xlsm", "Calc MS Excel 2007 VBA XML" },
265  { "xlsx", "Calc MS Excel 2007 XML" },
266  { "png", "calc_png_Export" },
267  { nullptr, nullptr }
268 };
269 
270 const ExtensionMap aImpressExtensionMap[] =
271 {
272  { "fodp", "OpenDocument Presentation Flat XML" },
273  { "html", "impress_html_Export" },
274  { "odg", "impress8_draw" },
275  { "odp", "impress8" },
276  { "otp", "impress8_template" },
277  { "pdf", "impress_pdf_Export" },
278  { "potm", "Impress MS PowerPoint 2007 XML Template" },
279  { "pot", "MS PowerPoint 97 Vorlage" },
280  { "pptm", "Impress MS PowerPoint 2007 XML VBA" },
281  { "pptx", "Impress MS PowerPoint 2007 XML" },
282  { "pps", "MS PowerPoint 97 Autoplay" },
283  { "ppt", "MS PowerPoint 97" },
284  { "svg", "impress_svg_Export" },
285  { "xhtml", "XHTML Impress File" },
286  { "png", "impress_png_Export"},
287  { nullptr, nullptr }
288 };
289 
290 const ExtensionMap aDrawExtensionMap[] =
291 {
292  { "fodg", "draw_ODG_FlatXML" },
293  { "html", "draw_html_Export" },
294  { "odg", "draw8" },
295  { "pdf", "draw_pdf_Export" },
296  { "svg", "draw_svg_Export" },
297  { "xhtml", "XHTML Draw File" },
298  { "png", "draw_png_Export"},
299  { nullptr, nullptr }
300 };
301 
302 static OUString getUString(const char* pString)
303 {
304  if (pString == nullptr)
305  return OUString();
306 
307  OString sString(pString, strlen(pString));
308  return OStringToOUString(sString, RTL_TEXTENCODING_UTF8);
309 }
310 
311 // Tolerate embedded \0s etc.
312 static char *convertOString(const OString &rStr)
313 {
314  char* pMemory = static_cast<char*>(malloc(rStr.getLength() + 1));
315  assert(pMemory); // don't tolerate failed allocations.
316  memcpy(pMemory, rStr.getStr(), rStr.getLength() + 1);
317  return pMemory;
318 }
319 
320 static char *convertOUString(std::u16string_view aStr)
321 {
322  return convertOString(OUStringToOString(aStr, RTL_TEXTENCODING_UTF8));
323 }
324 
326 static OUString getAbsoluteURL(const char* pURL)
327 {
328  OUString aURL(getUString(pURL));
329  if (aURL.isEmpty())
330  return aURL;
331 
332  // convert relative paths to absolute ones
333  OUString aWorkingDir;
334  osl_getProcessWorkingDir(&aWorkingDir.pData);
335  if (!aWorkingDir.endsWith("/"))
336  aWorkingDir += "/";
337 
338  try
339  {
340  return rtl::Uri::convertRelToAbs(aWorkingDir, aURL);
341  }
342  catch (const rtl::MalformedUriException &)
343  {
344  }
345 
346  return OUString();
347 }
348 
349 static uno::Any jsonToUnoAny(const boost::property_tree::ptree& aTree)
350 {
351  uno::Any aAny;
352  uno::Any aValue;
353  sal_Int32 nFields;
354  uno::Reference< reflection::XIdlField > aField;
355  boost::property_tree::ptree aNodeNull, aNodeValue, aNodeField;
356  const std::string& rType = aTree.get<std::string>("type", "");
357  const std::string& rValue = aTree.get<std::string>("value", "");
358  uno::Sequence< uno::Reference< reflection::XIdlField > > aFields;
359  uno::Reference< reflection:: XIdlClass > xIdlClass =
360  css::reflection::theCoreReflection::get(comphelper::getProcessComponentContext())->forName(OUString::fromUtf8(rType.c_str()));
361  if (xIdlClass.is())
362  {
363  uno::TypeClass aTypeClass = xIdlClass->getTypeClass();
364  xIdlClass->createObject(aAny);
365  aFields = xIdlClass->getFields();
366  nFields = aFields.getLength();
367  aNodeValue = aTree.get_child("value", aNodeNull);
368  if (nFields > 0 && aNodeValue != aNodeNull)
369  {
370  for (sal_Int32 itField = 0; itField < nFields; ++itField)
371  {
372  aField = aFields[itField];
373  aNodeField = aNodeValue.get_child(aField->getName().toUtf8().getStr(), aNodeNull);
374  if (aNodeField != aNodeNull)
375  {
376  aValue = jsonToUnoAny(aNodeField);
377  aField->set(aAny, aValue);
378  }
379  }
380  }
381  else if (!rValue.empty())
382  {
383  if (aTypeClass == uno::TypeClass_VOID)
384  aAny.clear();
385  else if (aTypeClass == uno::TypeClass_BYTE)
386  aAny <<= static_cast<sal_Int8>(OString(rValue.c_str()).toInt32());
387  else if (aTypeClass == uno::TypeClass_BOOLEAN)
388  aAny <<= OString(rValue.c_str()).toBoolean();
389  else if (aTypeClass == uno::TypeClass_SHORT)
390  aAny <<= static_cast<sal_Int16>(OString(rValue.c_str()).toInt32());
391  else if (aTypeClass == uno::TypeClass_UNSIGNED_SHORT)
392  aAny <<= static_cast<sal_uInt16>(OString(rValue.c_str()).toUInt32());
393  else if (aTypeClass == uno::TypeClass_LONG)
394  aAny <<= OString(rValue.c_str()).toInt32();
395  else if (aTypeClass == uno::TypeClass_UNSIGNED_LONG)
396  aAny <<= static_cast<sal_uInt32>(OString(rValue.c_str()).toInt32());
397  else if (aTypeClass == uno::TypeClass_FLOAT)
398  aAny <<= OString(rValue.c_str()).toFloat();
399  else if (aTypeClass == uno::TypeClass_DOUBLE)
400  aAny <<= OString(rValue.c_str()).toDouble();
401  else if (aTypeClass == uno::TypeClass_STRING)
402  aAny <<= OUString::fromUtf8(rValue.c_str());
403  }
404  }
405  return aAny;
406 }
407 
408 std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char* pJSON)
409 {
410  std::vector<beans::PropertyValue> aArguments;
411  if (pJSON && pJSON[0] != '\0')
412  {
413  boost::property_tree::ptree aTree, aNodeNull, aNodeValue;
414  std::stringstream aStream(pJSON);
415  boost::property_tree::read_json(aStream, aTree);
416 
417  for (const auto& rPair : aTree)
418  {
419  const std::string& rType = rPair.second.get<std::string>("type", "");
420  const std::string& rValue = rPair.second.get<std::string>("value", "");
421 
422  beans::PropertyValue aValue;
423  aValue.Name = OUString::fromUtf8(rPair.first.c_str());
424  if (rType == "string")
425  aValue.Value <<= OUString::fromUtf8(rValue.c_str());
426  else if (rType == "boolean")
427  aValue.Value <<= OString(rValue.c_str()).toBoolean();
428  else if (rType == "float")
429  aValue.Value <<= OString(rValue.c_str()).toFloat();
430  else if (rType == "long")
431  aValue.Value <<= OString(rValue.c_str()).toInt32();
432  else if (rType == "short")
433  aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
434  else if (rType == "unsigned short")
435  aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
436  else if (rType == "int64")
437  aValue.Value <<= OString(rValue.c_str()).toInt64();
438  else if (rType == "int32")
439  aValue.Value <<= OString(rValue.c_str()).toInt32();
440  else if (rType == "int16")
441  aValue.Value <<= sal_Int16(OString(rValue.c_str()).toInt32());
442  else if (rType == "uint64")
443  aValue.Value <<= OString(rValue.c_str()).toUInt64();
444  else if (rType == "uint32")
445  aValue.Value <<= OString(rValue.c_str()).toUInt32();
446  else if (rType == "uint16")
447  aValue.Value <<= sal_uInt16(OString(rValue.c_str()).toUInt32());
448  else if (rType == "[]byte")
449  {
450  aNodeValue = rPair.second.get_child("value", aNodeNull);
451  if (aNodeValue != aNodeNull && aNodeValue.size() == 0)
452  {
453  uno::Sequence< sal_Int8 > aSeqByte(reinterpret_cast<const sal_Int8*>(rValue.c_str()), rValue.size());
454  aValue.Value <<= aSeqByte;
455  }
456  }
457  else if (rType == "[]any")
458  {
459  aNodeValue = rPair.second.get_child("value", aNodeNull);
460  if (aNodeValue != aNodeNull && !aNodeValue.empty())
461  {
462  sal_Int32 itSeq = 0;
463  uno::Sequence< uno::Any > aSeq(aNodeValue.size());
464  for (const auto& rSeqPair : aNodeValue)
465  aSeq[itSeq++] = jsonToUnoAny(rSeqPair.second);
466  aValue.Value <<= aSeq;
467  }
468  }
469  else
470  SAL_WARN("desktop.lib", "jsonToPropertyValuesVector: unhandled type '"<<rType<<"'");
471  aArguments.push_back(aValue);
472  }
473  }
474  return aArguments;
475 }
476 
477 
478 static boost::property_tree::ptree unoAnyToPropertyTree(const uno::Any& anyItem)
479 {
480  boost::property_tree::ptree aTree;
481  OUString aType = anyItem.getValueTypeName();
482  aTree.put("type", aType.toUtf8().getStr());
483 
484  if (aType == "string")
485  aTree.put("value", anyItem.get<OUString>().toUtf8().getStr());
486  else if (aType == "unsigned long")
487  aTree.put("value", OString::number(anyItem.get<sal_uInt32>()).getStr());
488  else if (aType == "long")
489  aTree.put("value", OString::number(anyItem.get<sal_Int32>()).getStr());
490  else if (aType == "[]any")
491  {
492  uno::Sequence<uno::Any> aSeq;
493  if (anyItem >>= aSeq)
494  {
495  boost::property_tree::ptree aSubTree;
496 
497  for (auto i = 0; i < aSeq.getLength(); ++i)
498  {
499  aSubTree.add_child(OString::number(i).getStr(), unoAnyToPropertyTree(aSeq[i]));
500  }
501  aTree.add_child("value", aSubTree);
502  }
503  }
504 
505  // TODO: Add more as required
506 
507  return aTree;
508 }
509 
510 namespace desktop {
511 
512 RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
513 {
514  RectangleAndPart aRet;
515  if (rPayload.compare(0, 5, "EMPTY") == 0) // payload starts with "EMPTY"
516  {
519  aRet.m_nPart = std::stol(rPayload.substr(6));
520 
521  return aRet;
522  }
523 
524  std::istringstream aStream(rPayload);
525  tools::Long nLeft, nTop, nWidth, nHeight;
526  tools::Long nPart = INT_MIN;
527  char nComma;
529  {
530  aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight >> nComma >> nPart;
531  }
532  else
533  {
534  aStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
535  }
536 
537  if (nWidth > 0 && nHeight > 0)
538  {
539  // The top-left corner starts at (0, 0).
540  // Anything negative is invalid.
541  if (nLeft < 0)
542  {
543  nWidth += nLeft;
544  nLeft = 0;
545  }
546 
547  if (nTop < 0)
548  {
549  nHeight += nTop;
550  nTop = 0;
551  }
552 
553  if (nWidth > 0 && nHeight > 0)
554  {
555  aRet.m_aRectangle = tools::Rectangle(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
556  }
557  }
558  // else leave empty rect.
559 
560  aRet.m_nPart = nPart;
561  return aRet;
562 }
563 
564 RectangleAndPart& CallbackFlushHandler::CallbackData::setRectangleAndPart(const std::string& payload)
565 {
566  setRectangleAndPart(RectangleAndPart::Create(payload));
567 
568  // Return reference to the cached object.
569  return boost::get<RectangleAndPart>(PayloadObject);
570 }
571 
572 void CallbackFlushHandler::CallbackData::setRectangleAndPart(const RectangleAndPart& rRectAndPart)
573 {
574  PayloadString = rRectAndPart.toString().getStr();
575  PayloadObject = rRectAndPart;
576 }
577 
578 const RectangleAndPart& CallbackFlushHandler::CallbackData::getRectangleAndPart() const
579 {
580  assert(PayloadObject.which() == 1);
581  return boost::get<RectangleAndPart>(PayloadObject);
582 }
583 
584 boost::property_tree::ptree& CallbackFlushHandler::CallbackData::setJson(const std::string& payload)
585 {
586  boost::property_tree::ptree aTree;
587  std::stringstream aStream(payload);
588  boost::property_tree::read_json(aStream, aTree);
589 
590  // Let boost normalize the payload so it always matches the cache.
591  setJson(aTree);
592 
593  // Return reference to the cached object.
594  return boost::get<boost::property_tree::ptree>(PayloadObject);
595 }
596 
597 void CallbackFlushHandler::CallbackData::setJson(const boost::property_tree::ptree& rTree)
598 {
599  std::stringstream aJSONStream;
600  constexpr bool bPretty = false; // Don't waste time and bloat logs.
601  boost::property_tree::write_json(aJSONStream, rTree, bPretty);
602  PayloadString = boost::trim_copy(aJSONStream.str());
603 
604  PayloadObject = rTree;
605 }
606 
607 const boost::property_tree::ptree& CallbackFlushHandler::CallbackData::getJson() const
608 {
609  assert(PayloadObject.which() == 2);
610  return boost::get<boost::property_tree::ptree>(PayloadObject);
611 }
612 
613 bool CallbackFlushHandler::CallbackData::validate() const
614 {
615  switch (PayloadObject.which())
616  {
617  // Not cached.
618  case 0:
619  return true;
620 
621  // RectangleAndPart.
622  case 1:
623  return getRectangleAndPart().toString().getStr() == PayloadString;
624 
625  // Json.
626  case 2:
627  {
628  std::stringstream aJSONStream;
629  boost::property_tree::write_json(aJSONStream, getJson(), false);
630  const std::string aExpected = boost::trim_copy(aJSONStream.str());
631  return aExpected == PayloadString;
632  }
633 
634  default:
635  assert(!"Unknown variant type; please add an entry to validate.");
636  }
637 
638  return false;
639 }
640 
641 }
642 
643 namespace {
644 
645 bool lcl_isViewCallbackType(const int type)
646 {
647  switch (type)
648  {
649  case LOK_CALLBACK_CELL_VIEW_CURSOR:
650  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
651  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
652  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
653  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
654  return true;
655 
656  default:
657  return false;
658  }
659 }
660 
661 int lcl_getViewId(const std::string& payload)
662 {
663  // this is a cheap way how to get the viewId from a JSON message; proper
664  // parsing is terribly expensive, and we just need the viewId here
665  size_t viewIdPos = payload.find("viewId");
666  if (viewIdPos == std::string::npos)
667  return 0;
668 
669  size_t numberPos = payload.find(":", viewIdPos + 6);
670  if (numberPos == std::string::npos)
671  return 0;
672 
673  for (++numberPos; numberPos < payload.length(); ++numberPos)
674  {
675  if (payload[numberPos] == ',' || payload[numberPos] == '}' || (payload[numberPos] >= '0' && payload[numberPos] <= '9'))
676  break;
677  }
678 
679  if (numberPos < payload.length() && payload[numberPos] >= '0' && payload[numberPos] <= '9')
680  return strtol(payload.substr(numberPos).c_str(), nullptr, 10);
681 
682  return 0;
683 }
684 
685 int lcl_getViewId(const desktop::CallbackFlushHandler::CallbackData& rCallbackData)
686 {
687  if (rCallbackData.isCached())
688  return rCallbackData.getJson().get<int>("viewId");
689  return lcl_getViewId(rCallbackData.PayloadString);
690 }
691 
692 std::string extractCertificate(const std::string & certificate)
693 {
694  const std::string header("-----BEGIN CERTIFICATE-----");
695  const std::string footer("-----END CERTIFICATE-----");
696 
697  std::string result;
698 
699  size_t pos1 = certificate.find(header);
700  if (pos1 == std::string::npos)
701  return result;
702 
703  size_t pos2 = certificate.find(footer, pos1 + 1);
704  if (pos2 == std::string::npos)
705  return result;
706 
707  pos1 = pos1 + header.length();
708  pos2 = pos2 - pos1;
709 
710  return certificate.substr(pos1, pos2);
711 }
712 
713 std::string extractPrivateKey(const std::string & privateKey)
714 {
715  const std::string header("-----BEGIN PRIVATE KEY-----");
716  const std::string footer("-----END PRIVATE KEY-----");
717 
718  std::string result;
719 
720  size_t pos1 = privateKey.find(header);
721  if (pos1 == std::string::npos)
722  return result;
723 
724  size_t pos2 = privateKey.find(footer, pos1 + 1);
725  if (pos2 == std::string::npos)
726  return result;
727 
728  pos1 = pos1 + header.length();
729  pos2 = pos2 - pos1;
730 
731  return privateKey.substr(pos1, pos2);
732 }
733 
734 OUString lcl_getCurrentDocumentMimeType(const LibLODocument_Impl* pDocument)
735 {
736  SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
737  if (!pBaseModel)
738  return "";
739 
740  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
741  if (!pObjectShell)
742  return "";
743 
744  SfxMedium* pMedium = pObjectShell->GetMedium();
745  if (!pMedium)
746  return "";
747 
748  auto pFilter = pMedium->GetFilter();
749  if (!pFilter)
750  return "";
751 
752  return pFilter->GetMimeType();
753 }
754 
755 // Gets an undo manager to enter and exit undo context. Needed by ToggleOrientation
756 css::uno::Reference< css::document::XUndoManager > getUndoManager( const css::uno::Reference< css::frame::XFrame >& rxFrame )
757 {
758  const css::uno::Reference< css::frame::XController >& xController = rxFrame->getController();
759  if ( xController.is() )
760  {
761  const css::uno::Reference< css::frame::XModel >& xModel = xController->getModel();
762  if ( xModel.is() )
763  {
764  const css::uno::Reference< css::document::XUndoManagerSupplier > xSuppUndo( xModel, css::uno::UNO_QUERY_THROW );
765  return css::uno::Reference< css::document::XUndoManager >( xSuppUndo->getUndoManager(), css::uno::UNO_SET_THROW );
766  }
767  }
768 
769  return css::uno::Reference< css::document::XUndoManager > ();
770 }
771 
772 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
773 void ExecuteMarginLRChange(
774  const tools::Long nPageLeftMargin,
775  const tools::Long nPageRightMargin,
776  SvxLongLRSpaceItem* pPageLRMarginItem)
777 {
778  pPageLRMarginItem->SetLeft( nPageLeftMargin );
779  pPageLRMarginItem->SetRight( nPageRightMargin );
780  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_LRSPACE,
781  SfxCallMode::RECORD, { pPageLRMarginItem });
782 }
783 
784 // Adjusts page margins for Writer doc. Needed by ToggleOrientation
785 void ExecuteMarginULChange(
786  const tools::Long nPageTopMargin,
787  const tools::Long nPageBottomMargin,
788  SvxLongULSpaceItem* pPageULMarginItem)
789 {
790  pPageULMarginItem->SetUpper( nPageTopMargin );
791  pPageULMarginItem->SetLower( nPageBottomMargin );
792  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_ULSPACE,
793  SfxCallMode::RECORD, { pPageULMarginItem });
794 }
795 
796 // Main function which toggles page orientation of the Writer doc. Needed by ToggleOrientation
797 void ExecuteOrientationChange()
798 {
799  std::unique_ptr<SvxPageItem> pPageItem(new SvxPageItem(SID_ATTR_PAGE));
800  std::unique_ptr<SvxSizeItem> pPageSizeItem(new SvxSizeItem(SID_ATTR_PAGE_SIZE));
801  std::unique_ptr<SvxLongLRSpaceItem> pPageLRMarginItem(new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ));
802  std::unique_ptr<SvxLongULSpaceItem> pPageULMarginItem(new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ));
803  // 1mm in twips rounded
804  // This should be in sync with MINBODY in sw/source/uibase/sidebar/PageMarginControl.hxx
805  constexpr tools::Long MINBODY = 56;
806 
807  css::uno::Reference< css::document::XUndoManager > mxUndoManager(
808  getUndoManager( SfxViewFrame::Current()->GetFrame().GetFrameInterface() ) );
809 
810  if ( mxUndoManager.is() )
811  mxUndoManager->enterUndoContext( "" );
812 
813 
814  const SfxPoolItem* pItem;
815 
816 
817  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_SIZE, pItem);
818  pPageSizeItem.reset( static_cast<SvxSizeItem*>(pItem->Clone()) );
819 
820 
821 
822  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_LRSPACE, pItem);
823  pPageLRMarginItem.reset( static_cast<SvxLongLRSpaceItem*>(pItem->Clone()) );
824 
825 
826 
827  SfxViewFrame::Current()->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PAGE_ULSPACE, pItem);
828  pPageULMarginItem.reset( static_cast<SvxLongULSpaceItem*>(pItem->Clone()) );
829 
830 
831  {
832  bool bIsLandscape = false;
833  if ( pPageSizeItem->GetSize().Width() > pPageSizeItem->GetSize().Height())
834  bIsLandscape = true;
835 
836  // toggle page orientation
837  pPageItem->SetLandscape(!bIsLandscape);
838 
839 
840  // swap the width and height of the page size
841  const tools::Long nRotatedWidth = pPageSizeItem->GetSize().Height();
842  const tools::Long nRotatedHeight = pPageSizeItem->GetSize().Width();
843  pPageSizeItem->SetSize(Size(nRotatedWidth, nRotatedHeight));
844 
845 
846  // apply changed attributes
847  if (SfxViewShell::Current())
848  {
849  SfxViewShell::Current()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE,
850  SfxCallMode::RECORD, { pPageSizeItem.get(), pPageItem.get() });
851  }
852  }
853 
854 
855  // check, if margin values still fit to the changed page size.
856  // if not, adjust margin values
857  {
858  const tools::Long nML = pPageLRMarginItem->GetLeft();
859  const tools::Long nMR = pPageLRMarginItem->GetRight();
860  const tools::Long nTmpPW = nML + nMR + MINBODY;
861 
862  const tools::Long nPW = pPageSizeItem->GetSize().Width();
863 
864  if ( nTmpPW > nPW )
865  {
866  if ( nML <= nMR )
867  {
868  ExecuteMarginLRChange( pPageLRMarginItem->GetLeft(), nMR - (nTmpPW - nPW ), pPageLRMarginItem.get() );
869  }
870  else
871  {
872  ExecuteMarginLRChange( nML - (nTmpPW - nPW ), pPageLRMarginItem->GetRight(), pPageLRMarginItem.get() );
873  }
874  }
875 
876  const tools::Long nMT = pPageULMarginItem->GetUpper();
877  const tools::Long nMB = pPageULMarginItem->GetLower();
878  const tools::Long nTmpPH = nMT + nMB + MINBODY;
879 
880  const tools::Long nPH = pPageSizeItem->GetSize().Height();
881 
882  if ( nTmpPH > nPH )
883  {
884  if ( nMT <= nMB )
885  {
886  ExecuteMarginULChange( pPageULMarginItem->GetUpper(), nMB - ( nTmpPH - nPH ), pPageULMarginItem.get() );
887  }
888  else
889  {
890  ExecuteMarginULChange( nMT - ( nTmpPH - nPH ), pPageULMarginItem->GetLower(), pPageULMarginItem.get() );
891  }
892  }
893  }
894 
895  if ( mxUndoManager.is() )
896  mxUndoManager->leaveUndoContext();
897 }
898 
899 void setupSidebar(std::u16string_view sidebarDeckId = u"")
900 {
901  SfxViewShell* pViewShell = SfxViewShell::Current();
902  SfxViewFrame* pViewFrame = pViewShell ? pViewShell->GetViewFrame() : nullptr;
903  if (pViewFrame)
904  {
905  if (!pViewFrame->GetChildWindow(SID_SIDEBAR))
906  pViewFrame->SetChildWindow(SID_SIDEBAR, false /* create it */, true /* focus */);
907 
908  pViewFrame->ShowChildWindow(SID_SIDEBAR, true);
909 
910  // Force synchronous population of panels
911  SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
912  if (!pChild)
913  return;
914 
915  auto pDockingWin = dynamic_cast<sfx2::sidebar::SidebarDockingWindow *>(pChild->GetWindow());
916  if (!pDockingWin)
917  return;
918 
919  OUString currentDeckId = pDockingWin->GetSidebarController()->GetCurrentDeckId();
920 
921  // check if it is the chart deck id, if it is, don't switch to default deck
922  bool switchToDefault = true;
923 
924  if (currentDeckId == "ChartDeck")
925  switchToDefault = false;
926 
927  if (!sidebarDeckId.empty())
928  {
929  pDockingWin->GetSidebarController()->SwitchToDeck(sidebarDeckId);
930  }
931  else
932  {
933  if (switchToDefault)
934  pDockingWin->GetSidebarController()->SwitchToDefaultDeck();
935  }
936 
937  pDockingWin->SyncUpdate();
938  }
939  else
940  SetLastExceptionMsg("No view shell or sidebar");
941 }
942 
943 void hideSidebar()
944 {
945  SfxViewShell* pViewShell = SfxViewShell::Current();
946  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
947  if (pViewFrame)
948  pViewFrame->SetChildWindow(SID_SIDEBAR, false , false );
949  else
950  SetLastExceptionMsg("No view shell or sidebar");
951 }
952 
953 VclPtr<Window> getSidebarWindow()
954 {
955  VclPtr<Window> xRet;
956 
957  setupSidebar();
958  SfxViewShell* pViewShell = SfxViewShell::Current();
959  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
960  if (!pViewFrame)
961  return xRet;
962 
963  // really a SidebarChildWindow
964  SfxChildWindow *pChild = pViewFrame->GetChildWindow(SID_SIDEBAR);
965  if (!pChild)
966  return xRet;
967 
968  // really a SidebarDockingWindow
969  vcl::Window *pWin = pChild->GetWindow();
970  if (!pWin)
971  return xRet;
972  xRet = pWin;
973  return xRet;
974 }
975 
976 } // end anonymous namespace
977 
978 // Could be anonymous in principle, but for the unit testing purposes, we
979 // declare it in init.hxx.
980 OUString desktop::extractParameter(OUString& rOptions, std::u16string_view rName)
981 {
982  OUString aValue;
983 
984  OUString aNameEquals(OUString::Concat(rName) + "=");
985  OUString aCommaNameEquals(OUString::Concat(",") + rName + "=");
986 
987  int nIndex = -1;
988  if (rOptions.startsWith(aNameEquals))
989  {
990  size_t nLen = aNameEquals.getLength();
991  int nComma = rOptions.indexOf(",", nLen);
992  if (nComma >= 0)
993  {
994  aValue = rOptions.copy(nLen, nComma - nLen);
995  rOptions = rOptions.copy(nComma + 1);
996  }
997  else
998  {
999  aValue = rOptions.copy(nLen);
1000  rOptions.clear();
1001  }
1002  }
1003  else if ((nIndex = rOptions.indexOf(aCommaNameEquals)) >= 0)
1004  {
1005  size_t nLen = aCommaNameEquals.getLength();
1006  int nComma = rOptions.indexOf(",", nIndex + nLen);
1007  if (nComma >= 0)
1008  {
1009  aValue = rOptions.copy(nIndex + nLen, nComma - nIndex - nLen);
1010  rOptions = OUString::Concat(rOptions.subView(0, nIndex)) + rOptions.subView(nComma);
1011  }
1012  else
1013  {
1014  aValue = rOptions.copy(nIndex + nLen);
1015  rOptions = rOptions.copy(0, nIndex);
1016  }
1017  }
1018 
1019  return aValue;
1020 }
1021 
1022 extern "C"
1023 {
1024 
1025 static void doc_destroy(LibreOfficeKitDocument* pThis);
1026 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions);
1027 static int doc_getDocumentType(LibreOfficeKitDocument* pThis);
1028 static int doc_getParts(LibreOfficeKitDocument* pThis);
1029 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis);
1030 static int doc_getPart(LibreOfficeKitDocument* pThis);
1031 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart);
1032 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect);
1033 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
1034 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
1035 static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
1036 static void doc_paintTile(LibreOfficeKitDocument* pThis,
1037  unsigned char* pBuffer,
1038  const int nCanvasWidth, const int nCanvasHeight,
1039  const int nTilePosX, const int nTilePosY,
1040  const int nTileWidth, const int nTileHeight);
1041 #ifdef IOS
1042 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
1043  void* rCGContext,
1044  const int nCanvasWidth, const int nCanvasHeight,
1045  const int nTilePosX, const int nTilePosY,
1046  const int nTileWidth, const int nTileHeight);
1047 #endif
1048 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
1049  unsigned char* pBuffer,
1050  const int nPart,
1051  const int nCanvasWidth, const int nCanvasHeight,
1052  const int nTilePosX, const int nTilePosY,
1053  const int nTileWidth, const int nTileHeight);
1054 static int doc_getTileMode(LibreOfficeKitDocument* pThis);
1055 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
1056  long* pWidth,
1057  long* pHeight);
1058 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
1059  const char* pArguments);
1060 
1061 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
1062  LibreOfficeKitCallback pCallback,
1063  void* pData);
1064 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis,
1065  int nType,
1066  int nCharCode,
1067  int nKeyCode);
1068 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis,
1069  unsigned nWindowId,
1070  int nType,
1071  const char* pText);
1072 static void doc_removeTextContext(LibreOfficeKitDocument* pThis,
1073  unsigned nLOKWindowId,
1074  int nCharBefore,
1075  int nCharAfter);
1076 static void doc_sendDialogEvent(LibreOfficeKitDocument* pThis,
1077  unsigned long long int nLOKWindowId,
1078  const char* pArguments);
1079 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* pThis,
1080  unsigned nLOKWindowId,
1081  int nType,
1082  int nCharCode,
1083  int nKeyCode);
1084 static void doc_postMouseEvent (LibreOfficeKitDocument* pThis,
1085  int nType,
1086  int nX,
1087  int nY,
1088  int nCount,
1089  int nButtons,
1090  int nModifier);
1091 static void doc_postWindowMouseEvent (LibreOfficeKitDocument* pThis,
1092  unsigned nLOKWindowId,
1093  int nType,
1094  int nX,
1095  int nY,
1096  int nCount,
1097  int nButtons,
1098  int nModifier);
1099 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* pThis,
1100  unsigned nLOKWindowId,
1101  const char* pType,
1102  int nX,
1103  int nY,
1104  int nOffset);
1105 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis,
1106  const char* pCommand,
1107  const char* pArguments,
1108  bool bNotifyWhenFinished);
1109 static void doc_setWindowTextSelection(LibreOfficeKitDocument* pThis,
1110  unsigned nLOKWindowId,
1111  bool swap,
1112  int nX,
1113  int nY);
1114 static void doc_setTextSelection (LibreOfficeKitDocument* pThis,
1115  int nType,
1116  int nX,
1117  int nY);
1118 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis,
1119  const char* pMimeType,
1120  char** pUsedMimeType);
1121 static int doc_getSelectionType(LibreOfficeKitDocument* pThis);
1122 static int doc_getClipboard (LibreOfficeKitDocument* pThis,
1123  const char **pMimeTypes,
1124  size_t *pOutCount,
1125  char ***pOutMimeTypes,
1126  size_t **pOutSizes,
1127  char ***pOutStreams);
1128 static int doc_setClipboard (LibreOfficeKitDocument* pThis,
1129  const size_t nInCount,
1130  const char **pInMimeTypes,
1131  const size_t *pInSizes,
1132  const char **pInStreams);
1133 static bool doc_paste(LibreOfficeKitDocument* pThis,
1134  const char* pMimeType,
1135  const char* pData,
1136  size_t nSize);
1137 static void doc_setGraphicSelection (LibreOfficeKitDocument* pThis,
1138  int nType,
1139  int nX,
1140  int nY);
1141 static void doc_resetSelection (LibreOfficeKitDocument* pThis);
1142 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand);
1143 static void doc_setClientZoom(LibreOfficeKitDocument* pThis,
1144  int nTilePixelWidth,
1145  int nTilePixelHeight,
1146  int nTileTwipWidth,
1147  int nTileTwipHeight);
1148 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight);
1149 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden);
1150 static int doc_createView(LibreOfficeKitDocument* pThis);
1151 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis, const char* pOptions);
1152 static void doc_destroyView(LibreOfficeKitDocument* pThis, int nId);
1153 static void doc_setView(LibreOfficeKitDocument* pThis, int nId);
1154 static int doc_getView(LibreOfficeKitDocument* pThis);
1155 static int doc_getViewsCount(LibreOfficeKitDocument* pThis);
1156 static bool doc_getViewIds(LibreOfficeKitDocument* pThis, int* pArray, size_t nSize);
1157 static void doc_setViewLanguage(LibreOfficeKitDocument* pThis, int nId, const char* language);
1158 static unsigned char* doc_renderFontOrientation(LibreOfficeKitDocument* pThis,
1159  const char *pFontName,
1160  const char *pChar,
1161  int* pFontWidth,
1162  int* pFontHeight,
1163  int pOrientation);
1164 static unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
1165  const char *pFontName,
1166  const char *pChar,
1167  int* pFontWidth,
1168  int* pFontHeight);
1169 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart);
1170 
1171 static void doc_paintWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1172  const int nX, const int nY,
1173  const int nWidth, const int nHeight);
1174 
1175 static void doc_paintWindowDPI(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1176  const int nX, const int nY,
1177  const int nWidth, const int nHeight,
1178  const double fDPIScale);
1179 
1180 static void doc_paintWindowForView(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, unsigned char* pBuffer,
1181  const int nX, const int nY,
1182  const int nWidth, const int nHeight,
1183  const double fDPIScale, int viewId);
1184 
1185 static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned
1186  nLOKWindowId, int nAction, const char* pData);
1187 
1188 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
1189 
1190 static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
1191  const unsigned char* pCertificateBinary,
1192  const int nCertificateBinarySize,
1193  const unsigned char* pPrivateKeyBinary,
1194  const int nPrivateKeyBinarySize);
1195 
1196 static bool doc_addCertificate(LibreOfficeKitDocument* pThis,
1197  const unsigned char* pCertificateBinary,
1198  const int nCertificateBinarySize);
1199 
1200 static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
1201 
1202 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput);
1203 
1204 static void doc_resizeWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
1205  const int nWidth, const int nHeight);
1206 
1207 static void doc_completeFunction(LibreOfficeKitDocument* pThis, const char*);
1208 
1209 
1210 static void doc_sendFormFieldEvent(LibreOfficeKitDocument* pThis,
1211  const char* pArguments);
1212 } // extern "C"
1213 
1214 namespace {
1215 ITiledRenderable* getTiledRenderable(LibreOfficeKitDocument* pThis)
1216 {
1217  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
1218  return dynamic_cast<ITiledRenderable*>(pDocument->mxComponent.get());
1219 }
1220 
1221 #ifndef IOS
1222 
1223 /*
1224  * Unfortunately clipboard creation using UNO is insanely baroque.
1225  * we also need to ensure that this works for the first view which
1226  * has no clear 'createView' called for it (unfortunately).
1227  */
1228 rtl::Reference<LOKClipboard> forceSetClipboardForCurrentView(LibreOfficeKitDocument *pThis)
1229 {
1230  ITiledRenderable* pDoc = getTiledRenderable(pThis);
1232 
1233  SAL_INFO("lok", "Set to clipboard for view " << xClip.get());
1234  // FIXME: using a hammer here - should not be necessary if all tests used createView.
1235  pDoc->setClipboard(uno::Reference<datatransfer::clipboard::XClipboard>(xClip->getXI(), UNO_QUERY));
1236 
1237  return xClip;
1238 }
1239 
1240 #endif
1241 
1242 } // anonymous namespace
1243 
1244 LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent, int nDocumentId)
1245  : mxComponent(xComponent)
1246  , mnDocumentId(nDocumentId)
1247 {
1248  assert(nDocumentId != -1 && "Cannot set mnDocumentId to -1");
1249 
1251  if (!m_pDocumentClass)
1252  {
1253  m_pDocumentClass = std::make_shared<LibreOfficeKitDocumentClass>();
1254 
1255  m_pDocumentClass->nSize = sizeof(LibreOfficeKitDocumentClass);
1256 
1257  m_pDocumentClass->destroy = doc_destroy;
1258  m_pDocumentClass->saveAs = doc_saveAs;
1259  m_pDocumentClass->getDocumentType = doc_getDocumentType;
1260  m_pDocumentClass->getParts = doc_getParts;
1261  m_pDocumentClass->getPartPageRectangles = doc_getPartPageRectangles;
1262  m_pDocumentClass->getPart = doc_getPart;
1263  m_pDocumentClass->setPart = doc_setPart;
1264  m_pDocumentClass->selectPart = doc_selectPart;
1265  m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
1266  m_pDocumentClass->getPartName = doc_getPartName;
1267  m_pDocumentClass->setPartMode = doc_setPartMode;
1268  m_pDocumentClass->paintTile = doc_paintTile;
1269 #ifdef IOS
1270  m_pDocumentClass->paintTileToCGContext = doc_paintTileToCGContext;
1271 #endif
1272  m_pDocumentClass->paintPartTile = doc_paintPartTile;
1273  m_pDocumentClass->getTileMode = doc_getTileMode;
1274  m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
1275  m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
1276  m_pDocumentClass->registerCallback = doc_registerCallback;
1277  m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
1278  m_pDocumentClass->postWindowExtTextInputEvent = doc_postWindowExtTextInputEvent;
1279  m_pDocumentClass->removeTextContext = doc_removeTextContext;
1280  m_pDocumentClass->postWindowKeyEvent = doc_postWindowKeyEvent;
1281  m_pDocumentClass->postMouseEvent = doc_postMouseEvent;
1282  m_pDocumentClass->postWindowMouseEvent = doc_postWindowMouseEvent;
1283  m_pDocumentClass->sendDialogEvent = doc_sendDialogEvent;
1284  m_pDocumentClass->postUnoCommand = doc_postUnoCommand;
1285  m_pDocumentClass->setTextSelection = doc_setTextSelection;
1286  m_pDocumentClass->setWindowTextSelection = doc_setWindowTextSelection;
1287  m_pDocumentClass->getTextSelection = doc_getTextSelection;
1288  m_pDocumentClass->getSelectionType = doc_getSelectionType;
1289  m_pDocumentClass->getClipboard = doc_getClipboard;
1290  m_pDocumentClass->setClipboard = doc_setClipboard;
1291  m_pDocumentClass->paste = doc_paste;
1292  m_pDocumentClass->setGraphicSelection = doc_setGraphicSelection;
1293  m_pDocumentClass->resetSelection = doc_resetSelection;
1294  m_pDocumentClass->getCommandValues = doc_getCommandValues;
1295  m_pDocumentClass->setClientZoom = doc_setClientZoom;
1296  m_pDocumentClass->setClientVisibleArea = doc_setClientVisibleArea;
1297  m_pDocumentClass->setOutlineState = doc_setOutlineState;
1298 
1299  m_pDocumentClass->createView = doc_createView;
1300  m_pDocumentClass->destroyView = doc_destroyView;
1301  m_pDocumentClass->setView = doc_setView;
1302  m_pDocumentClass->getView = doc_getView;
1303  m_pDocumentClass->getViewsCount = doc_getViewsCount;
1304  m_pDocumentClass->getViewIds = doc_getViewIds;
1305 
1306  m_pDocumentClass->renderFont = doc_renderFont;
1307  m_pDocumentClass->renderFontOrientation = doc_renderFontOrientation;
1308  m_pDocumentClass->getPartHash = doc_getPartHash;
1309 
1310  m_pDocumentClass->paintWindow = doc_paintWindow;
1311  m_pDocumentClass->paintWindowDPI = doc_paintWindowDPI;
1312  m_pDocumentClass->paintWindowForView = doc_paintWindowForView;
1313  m_pDocumentClass->postWindow = doc_postWindow;
1314  m_pDocumentClass->resizeWindow = doc_resizeWindow;
1315 
1316  m_pDocumentClass->setViewLanguage = doc_setViewLanguage;
1317 
1318  m_pDocumentClass->getPartInfo = doc_getPartInfo;
1319 
1320  m_pDocumentClass->insertCertificate = doc_insertCertificate;
1321  m_pDocumentClass->addCertificate = doc_addCertificate;
1322  m_pDocumentClass->getSignatureState = doc_getSignatureState;
1323 
1324  m_pDocumentClass->renderShapeSelection = doc_renderShapeSelection;
1325  m_pDocumentClass->postWindowGestureEvent = doc_postWindowGestureEvent;
1326 
1327  m_pDocumentClass->createViewWithOptions = doc_createViewWithOptions;
1328  m_pDocumentClass->completeFunction = doc_completeFunction;
1329 
1330  m_pDocumentClass->sendFormFieldEvent = doc_sendFormFieldEvent;
1331 
1333  }
1334  pClass = m_pDocumentClass.get();
1335 
1336 #ifndef IOS
1337  forceSetClipboardForCurrentView(this);
1338 #endif
1339 }
1340 
1342 {
1343  try
1344  {
1345  mxComponent->dispose();
1346  }
1347  catch (const css::lang::DisposedException&)
1348  {
1349  TOOLS_WARN_EXCEPTION("lok", "failed to dispose document");
1350  }
1351 }
1352 
1353 static OUString getGenerator()
1354 {
1355  OUString sGenerator(
1356  Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION%PRODUCTEXTENSION (%1)"));
1357  OUString os("$_OS");
1358  ::rtl::Bootstrap::expandMacros(os);
1359  return sGenerator.replaceFirst("%1", os);
1360 }
1361 
1362 extern "C" {
1363 
1364 CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, LibreOfficeKitCallback pCallback, void* pData)
1365  : Idle( "lokit timer callback" ),
1366  m_pDocument(pDocument),
1367  m_pCallback(pCallback),
1368  m_pData(pData),
1369  m_nDisableCallbacks(0)
1370 {
1371  SetPriority(TaskPriority::POST_PAINT);
1372 
1373  // Add the states that are safe to skip duplicates on, even when
1374  // not consequent (i.e. do no emit them if unchanged from last).
1375  m_states.emplace(LOK_CALLBACK_TEXT_SELECTION, "NIL");
1376  m_states.emplace(LOK_CALLBACK_GRAPHIC_SELECTION, "NIL");
1377  m_states.emplace(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, "NIL");
1378  m_states.emplace(LOK_CALLBACK_STATE_CHANGED, "NIL");
1379  m_states.emplace(LOK_CALLBACK_MOUSE_POINTER, "NIL");
1380  m_states.emplace(LOK_CALLBACK_CELL_CURSOR, "NIL");
1381  m_states.emplace(LOK_CALLBACK_CELL_FORMULA, "NIL");
1382  m_states.emplace(LOK_CALLBACK_CELL_ADDRESS, "NIL");
1383  m_states.emplace(LOK_CALLBACK_CURSOR_VISIBLE, "NIL");
1384  m_states.emplace(LOK_CALLBACK_SET_PART, "NIL");
1385 
1386  Start();
1387 }
1388 
1390 {
1391  Stop();
1392 }
1393 
1394 void CallbackFlushHandler::callback(const int type, const char* payload, void* data)
1395 {
1396  CallbackFlushHandler* self = static_cast<CallbackFlushHandler*>(data);
1397  if (self)
1398  {
1399  self->queue(type, payload);
1400  }
1401 }
1402 
1403 void CallbackFlushHandler::queue(const int type, const char* data)
1404 {
1405  comphelper::ProfileZone aZone("CallbackFlushHandler::queue");
1406 
1407  CallbackData aCallbackData(type, (data ? data : "(nil)"));
1408  const std::string& payload = aCallbackData.PayloadString;
1409  SAL_INFO("lok", "Queue: [" << type << "]: [" << payload << "] on " << m_queue.size() << " entries.");
1410 
1411  bool bIsChartActive = false;
1412  if (type == LOK_CALLBACK_GRAPHIC_SELECTION)
1413  {
1414  LokChartHelper aChartHelper(SfxViewShell::Current());
1415  bIsChartActive = aChartHelper.GetWindow() != nullptr;
1416  }
1417 
1418  if (callbacksDisabled() && !bIsChartActive)
1419  {
1420  // We drop notifications when this is set, except for important ones.
1421  // When we issue a complex command (such as .uno:InsertAnnotation)
1422  // there will be multiple notifications. On the first invalidation
1423  // we will start painting, but other events will get fired
1424  // while the complex command in question executes.
1425  // We don't want to suppress everything here on the wrong assumption
1426  // that no new events are fired during painting.
1427  if (type != LOK_CALLBACK_STATE_CHANGED &&
1428  type != LOK_CALLBACK_INVALIDATE_TILES &&
1429  type != LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1430  type != LOK_CALLBACK_CURSOR_VISIBLE &&
1431  type != LOK_CALLBACK_VIEW_CURSOR_VISIBLE &&
1432  type != LOK_CALLBACK_TEXT_SELECTION &&
1433  type != LOK_CALLBACK_TEXT_SELECTION_START &&
1434  type != LOK_CALLBACK_TEXT_SELECTION_END &&
1435  type != LOK_CALLBACK_REFERENCE_MARKS)
1436  {
1437  SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << payload << "].");
1438  return;
1439  }
1440 
1441  // In Writer we drop all notifications during painting.
1442  if (doc_getDocumentType(m_pDocument) == LOK_DOCTYPE_TEXT)
1443  return;
1444  }
1445 
1446  // Suppress invalid payloads.
1447  if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1448  payload.find(", 0, 0, ") != std::string::npos &&
1449  payload.find("\"hyperlink\":\"\"") == std::string::npos &&
1450  payload.find("\"hyperlink\": {}") == std::string::npos)
1451  {
1452  // The cursor position is often the relative coordinates of the widget
1453  // issuing it, instead of the absolute one that we expect.
1454  // This is temporary however, and, once the control is created and initialized
1455  // correctly, it eventually emits the correct absolute coordinates.
1456  SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1457  return;
1458  }
1459 
1460  std::unique_lock<std::mutex> lock(m_mutex);
1461 
1462  // drop duplicate callbacks for the listed types
1463  switch (type)
1464  {
1465  case LOK_CALLBACK_TEXT_SELECTION_START:
1466  case LOK_CALLBACK_TEXT_SELECTION_END:
1467  case LOK_CALLBACK_TEXT_SELECTION:
1468  case LOK_CALLBACK_GRAPHIC_SELECTION:
1469  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1470  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1471  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1472  case LOK_CALLBACK_STATE_CHANGED:
1473  case LOK_CALLBACK_MOUSE_POINTER:
1474  case LOK_CALLBACK_CELL_CURSOR:
1475  case LOK_CALLBACK_CELL_VIEW_CURSOR:
1476  case LOK_CALLBACK_CELL_FORMULA:
1477  case LOK_CALLBACK_CELL_ADDRESS:
1478  case LOK_CALLBACK_CELL_SELECTION_AREA:
1479  case LOK_CALLBACK_CURSOR_VISIBLE:
1480  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1481  case LOK_CALLBACK_SET_PART:
1482  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1483  case LOK_CALLBACK_INVALIDATE_HEADER:
1484  case LOK_CALLBACK_WINDOW:
1485  case LOK_CALLBACK_CALC_FUNCTION_LIST:
1486  case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
1487  {
1488  const auto& pos = std::find_if(m_queue.rbegin(), m_queue.rend(),
1489  [type] (const queue_type::value_type& elem) { return (elem.Type == type); });
1490 
1491  if (pos != m_queue.rend() && pos->PayloadString == payload)
1492  {
1493  SAL_INFO("lok", "Skipping queue duplicate [" << type << + "]: [" << payload << "].");
1494  return;
1495  }
1496  }
1497  break;
1498  }
1499 
1500  if (type == LOK_CALLBACK_TEXT_SELECTION && payload.empty())
1501  {
1502  const auto& posStart = std::find_if(m_queue.rbegin(), m_queue.rend(),
1503  [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_START); });
1504  if (posStart != m_queue.rend())
1505  posStart->PayloadString.clear();
1506 
1507  const auto& posEnd = std::find_if(m_queue.rbegin(), m_queue.rend(),
1508  [] (const queue_type::value_type& elem) { return (elem.Type == LOK_CALLBACK_TEXT_SELECTION_END); });
1509  if (posEnd != m_queue.rend())
1510  posEnd->PayloadString.clear();
1511  }
1512 
1513  // When payload is empty discards any previous state.
1514  if (payload.empty())
1515  {
1516  switch (type)
1517  {
1518  case LOK_CALLBACK_TEXT_SELECTION_START:
1519  case LOK_CALLBACK_TEXT_SELECTION_END:
1520  case LOK_CALLBACK_TEXT_SELECTION:
1521  case LOK_CALLBACK_GRAPHIC_SELECTION:
1522  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1523  case LOK_CALLBACK_INVALIDATE_TILES:
1524  if (removeAll(
1525  [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1526  SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1527  break;
1528  }
1529  }
1530  else
1531  {
1532  switch (type)
1533  {
1534  // These are safe to use the latest state and ignore previous
1535  // ones (if any) since the last overrides previous ones.
1536  case LOK_CALLBACK_TEXT_SELECTION_START:
1537  case LOK_CALLBACK_TEXT_SELECTION_END:
1538  case LOK_CALLBACK_TEXT_SELECTION:
1539  case LOK_CALLBACK_MOUSE_POINTER:
1540  case LOK_CALLBACK_CELL_CURSOR:
1541  case LOK_CALLBACK_CELL_FORMULA:
1542  case LOK_CALLBACK_CELL_ADDRESS:
1543  case LOK_CALLBACK_CURSOR_VISIBLE:
1544  case LOK_CALLBACK_SET_PART:
1545  case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
1546  case LOK_CALLBACK_RULER_UPDATE:
1547  {
1548  if (removeAll(
1549  [type](const queue_type::value_type& elem) { return (elem.Type == type); }))
1550  SAL_INFO("lok", "Removed dups of [" << type << "]: [" << payload << "].");
1551  }
1552  break;
1553 
1554  // These are safe to use the latest state and ignore previous
1555  // ones (if any) since the last overrides previous ones,
1556  // but only if the view is the same.
1557  case LOK_CALLBACK_CELL_VIEW_CURSOR:
1558  case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION:
1559  case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
1560  case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
1561  case LOK_CALLBACK_TEXT_VIEW_SELECTION:
1562  case LOK_CALLBACK_VIEW_CURSOR_VISIBLE:
1563  case LOK_CALLBACK_CALC_FUNCTION_LIST:
1564  case LOK_CALLBACK_FORM_FIELD_BUTTON:
1565  {
1566  // deleting the duplicate of visible cursor message can cause hyperlink popup not to show up on second/or more click on the same place.
1567  // If the hyperlink is not empty we can bypass that to show the popup
1568  const bool hyperLinkException = type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR &&
1569  payload.find("\"hyperlink\":\"\"") == std::string::npos &&
1570  payload.find("\"hyperlink\": {}") == std::string::npos;
1571  const int nViewId = lcl_getViewId(payload);
1572  removeAll(
1573  [type, nViewId, hyperLinkException] (const queue_type::value_type& elem) {
1574  return (elem.Type == type && nViewId == lcl_getViewId(elem) && !hyperLinkException);
1575  }
1576  );
1577  }
1578  break;
1579 
1580  case LOK_CALLBACK_INVALIDATE_TILES:
1581  if (processInvalidateTilesEvent(aCallbackData))
1582  return;
1583  break;
1584 
1585  // State changes with same name override previous ones with a different value.
1586  // Ex. ".uno:PageStatus=Slide 20 of 83" overwrites any previous PageStatus.
1587  case LOK_CALLBACK_STATE_CHANGED:
1588  {
1589  // Compare the state name=value and overwrite earlier entries with same name.
1590  const auto pos = payload.find('=');
1591  if (pos != std::string::npos)
1592  {
1593  const std::string name = payload.substr(0, pos + 1);
1594  // This is needed because otherwise it creates some problems when
1595  // a save occurs while a cell is still edited in Calc.
1596  if (name != ".uno:ModifiedStatus=")
1597  {
1598  removeAll(
1599  [type, &name] (const queue_type::value_type& elem) {
1600  return (elem.Type == type) && (elem.PayloadString.compare(0, name.size(), name) == 0);
1601  }
1602  );
1603  }
1604  }
1605  }
1606  break;
1607 
1608  case LOK_CALLBACK_WINDOW:
1609  if (processWindowEvent(aCallbackData))
1610  return;
1611  break;
1612 
1613  case LOK_CALLBACK_GRAPHIC_SELECTION:
1614  {
1615  // remove only selection ranges and 'EMPTY' messages
1616  // always send 'INPLACE' and 'INPLACE EXIT' messages
1617  removeAll([type, payload] (const queue_type::value_type& elem)
1618  { return (elem.Type == type && elem.PayloadString[0] != 'I'); });
1619  }
1620  break;
1621  }
1622  }
1623 
1624  // Validate that the cached data and the payload string are identical.
1625  assert(aCallbackData.validate() && "Cached callback payload object and string mismatch!");
1626  m_queue.emplace_back(aCallbackData);
1627  SAL_INFO("lok", "Queued #" << (m_queue.size() - 1) <<
1628  " [" << type << "]: [" << payload << "] to have " << m_queue.size() << " entries.");
1629 
1630 #ifdef DBG_UTIL
1631  {
1632  // Dump the queue state and validate cached data.
1633  int i = 1;
1634  std::ostringstream oss;
1635  if (m_queue.empty())
1636  oss << "Empty";
1637  else
1638  oss << m_queue.size() << " items\n";
1639  for (const CallbackData& c : m_queue)
1640  oss << i++ << ": [" << c.Type << "] [" << c.PayloadString << "].\n";
1641  SAL_INFO("lok", "Current Queue: " << oss.str());
1642  assert(
1643  std::all_of(
1644  m_queue.begin(), m_queue.end(),
1645  [](const CallbackData& c) { return c.validate(); }));
1646  }
1647 #endif
1648 
1649  lock.unlock();
1650  if (!IsActive())
1651  {
1652  Start();
1653  }
1654 }
1655 
1657 {
1658  const std::string& payload = aCallbackData.PayloadString;
1659  const int type = aCallbackData.Type;
1660 
1661  RectangleAndPart& rcNew = aCallbackData.setRectangleAndPart(payload);
1662  if (rcNew.isEmpty())
1663  {
1664  SAL_INFO("lok", "Skipping invalid event [" << type << "]: [" << payload << "].");
1665  return true;
1666  }
1667 
1668  // If we have to invalidate all tiles, we can skip any new tile invalidation.
1669  // Find the last INVALIDATE_TILES entry, if any to see if it's invalidate-all.
1670  const auto& pos
1671  = std::find_if(m_queue.rbegin(), m_queue.rend(), [](const queue_type::value_type& elem) {
1672  return (elem.Type == LOK_CALLBACK_INVALIDATE_TILES);
1673  });
1674  if (pos != m_queue.rend())
1675  {
1676  const RectangleAndPart& rcOld = pos->getRectangleAndPart();
1677  if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
1678  {
1679  SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1680  << "] since all tiles need to be invalidated.");
1681  return true;
1682  }
1683 
1684  if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
1685  {
1686  // If fully overlapping.
1687  if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1688  {
1689  SAL_INFO("lok", "Skipping queue [" << type << "]: [" << payload
1690  << "] since overlaps existing all-parts.");
1691  return true;
1692  }
1693  }
1694  }
1695 
1696  if (rcNew.isInfinite())
1697  {
1698  SAL_INFO("lok", "Have Empty [" << type << "]: [" << payload
1699  << "] so removing all with part " << rcNew.m_nPart << ".");
1700  removeAll([&rcNew](const queue_type::value_type& elem) {
1701  if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1702  {
1703  // Remove exiting if new is all-encompassing, or if of the same part.
1704  const RectangleAndPart rcOld = RectangleAndPart::Create(elem.PayloadString);
1705  return (rcNew.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart);
1706  }
1707 
1708  // Keep others.
1709  return false;
1710  });
1711  }
1712  else
1713  {
1714  const auto rcOrig = rcNew;
1715 
1716  SAL_INFO("lok", "Have [" << type << "]: [" << payload << "] so merging overlapping.");
1717  removeAll([&rcNew](const queue_type::value_type& elem) {
1718  if (elem.Type == LOK_CALLBACK_INVALIDATE_TILES)
1719  {
1720  const RectangleAndPart& rcOld = elem.getRectangleAndPart();
1721  if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
1722  {
1723  SAL_INFO("lok", "Nothing to merge between new: "
1724  << rcNew.toString() << ", and old: " << rcOld.toString());
1725  return false;
1726  }
1727 
1728  if (rcNew.m_nPart == -1)
1729  {
1730  // Don't merge unless fully overlapped.
1731  SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
1732  << "?");
1733  if (rcNew.m_aRectangle.IsInside(rcOld.m_aRectangle))
1734  {
1735  SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1736  << rcOld.toString() << ".");
1737  return true;
1738  }
1739  }
1740  else if (rcOld.m_nPart == -1)
1741  {
1742  // Don't merge unless fully overlapped.
1743  SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
1744  << "?");
1745  if (rcOld.m_aRectangle.IsInside(rcNew.m_aRectangle))
1746  {
1747  SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
1748  << rcOld.toString() << ".");
1749  return true;
1750  }
1751  }
1752  else
1753  {
1754  const tools::Rectangle rcOverlap
1755  = rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
1756  const bool bOverlap = !rcOverlap.IsEmpty();
1757  SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
1758  << " => " << rcOverlap.toString()
1759  << " Overlap: " << bOverlap);
1760  if (bOverlap)
1761  {
1762  rcNew.m_aRectangle.Union(rcOld.m_aRectangle);
1763  SAL_INFO("lok", "Merged: " << rcNew.toString());
1764  return true;
1765  }
1766  }
1767  }
1768 
1769  // Keep others.
1770  return false;
1771  });
1772 
1773  if (rcNew.m_aRectangle != rcOrig.m_aRectangle)
1774  {
1775  SAL_INFO("lok", "Replacing: " << rcOrig.toString() << " by " << rcNew.toString());
1776  if (rcNew.m_aRectangle.GetWidth() < rcOrig.m_aRectangle.GetWidth()
1777  || rcNew.m_aRectangle.GetHeight() < rcOrig.m_aRectangle.GetHeight())
1778  {
1779  SAL_WARN("lok", "Error: merged rect smaller.");
1780  }
1781  }
1782  }
1783 
1784  aCallbackData.setRectangleAndPart(rcNew);
1785  // Queue this one.
1786  return false;
1787 }
1788 
1789 bool CallbackFlushHandler::processWindowEvent(CallbackData& aCallbackData)
1790 {
1791  const std::string& payload = aCallbackData.PayloadString;
1792  const int type = aCallbackData.Type;
1793 
1794  boost::property_tree::ptree& aTree = aCallbackData.setJson(payload);
1795  const unsigned nLOKWindowId = aTree.get<unsigned>("id", 0);
1796  const std::string aAction = aTree.get<std::string>("action", "");
1797  if (aAction == "invalidate")
1798  {
1799  std::string aRectStr = aTree.get<std::string>("rectangle", "");
1800  // no 'rectangle' field => invalidate all of the window =>
1801  // remove all previous window part invalidations
1802  if (aRectStr.empty())
1803  {
1804  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1805  if (elem.Type == LOK_CALLBACK_WINDOW)
1806  {
1807  const boost::property_tree::ptree& aOldTree = elem.getJson();
1808  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1809  && aOldTree.get<std::string>("action", "") == "invalidate")
1810  {
1811  return true;
1812  }
1813  }
1814  return false;
1815  });
1816  }
1817  else
1818  {
1819  // if we have to invalidate all of the window, ignore
1820  // any part invalidation message
1821  const auto invAllExist = std::any_of(m_queue.rbegin(), m_queue.rend(),
1822  [&nLOKWindowId] (const queue_type::value_type& elem)
1823  {
1824  if (elem.Type != LOK_CALLBACK_WINDOW)
1825  return false;
1826 
1827  const boost::property_tree::ptree& aOldTree = elem.getJson();
1828  return nLOKWindowId == aOldTree.get<unsigned>("id", 0)
1829  && aOldTree.get<std::string>("action", "") == "invalidate"
1830  && aOldTree.get<std::string>("rectangle", "").empty();
1831  });
1832 
1833  // we found a invalidate-all window callback
1834  if (invAllExist)
1835  {
1836  SAL_INFO("lok.dialog", "Skipping queue ["
1837  << type << "]: [" << payload
1838  << "] since whole window needs to be invalidated.");
1839  return true;
1840  }
1841 
1842  std::istringstream aRectStream(aRectStr);
1843  tools::Long nLeft, nTop, nWidth, nHeight;
1844  char nComma;
1845  aRectStream >> nLeft >> nComma >> nTop >> nComma >> nWidth >> nComma >> nHeight;
1846  tools::Rectangle aNewRect(nLeft, nTop, nLeft + nWidth, nTop + nHeight);
1847  bool currentIsRedundant = false;
1848  removeAll([&aNewRect, &nLOKWindowId,
1849  &currentIsRedundant](const queue_type::value_type& elem) {
1850  if (elem.Type != LOK_CALLBACK_WINDOW)
1851  return false;
1852 
1853  const boost::property_tree::ptree& aOldTree = elem.getJson();
1854  if (aOldTree.get<std::string>("action", "") == "invalidate")
1855  {
1856  // Not possible that we encounter an empty rectangle here; we already handled this case above.
1857  std::istringstream aOldRectStream(aOldTree.get<std::string>("rectangle", ""));
1858  tools::Long nOldLeft, nOldTop, nOldWidth, nOldHeight;
1859  char nOldComma;
1860  aOldRectStream >> nOldLeft >> nOldComma >> nOldTop >> nOldComma >> nOldWidth
1861  >> nOldComma >> nOldHeight;
1862  const tools::Rectangle aOldRect = tools::Rectangle(
1863  nOldLeft, nOldTop, nOldLeft + nOldWidth, nOldTop + nOldHeight);
1864 
1865  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1866  {
1867  if (aNewRect == aOldRect)
1868  {
1869  SAL_INFO("lok.dialog", "Duplicate rect [" << aNewRect.toString()
1870  << "]. Skipping new.");
1871  // We have a rectangle in the queue already that makes the current Callback useless.
1872  currentIsRedundant = true;
1873  return false;
1874  }
1875  // new one engulfs the old one?
1876  else if (aNewRect.IsInside(aOldRect))
1877  {
1878  SAL_INFO("lok.dialog",
1879  "New rect [" << aNewRect.toString() << "] engulfs old ["
1880  << aOldRect.toString() << "]. Replacing old.");
1881  return true;
1882  }
1883  // old one engulfs the new one?
1884  else if (aOldRect.IsInside(aNewRect))
1885  {
1886  SAL_INFO("lok.dialog",
1887  "Old rect [" << aOldRect.toString() << "] engulfs new ["
1888  << aNewRect.toString() << "]. Skipping new.");
1889  // We have a rectangle in the queue already that makes the current Callback useless.
1890  currentIsRedundant = true;
1891  return false;
1892  }
1893  else
1894  {
1895  // Overlapping rects.
1896  const tools::Rectangle aPreMergeRect = aNewRect;
1897  aNewRect.Union(aOldRect);
1898  SAL_INFO("lok.dialog", "Merging rects ["
1899  << aPreMergeRect.toString() << "] & ["
1900  << aOldRect.toString() << "] = ["
1901  << aNewRect.toString()
1902  << "]. Replacing old.");
1903  return true;
1904  }
1905  }
1906  }
1907 
1908  // keep rest
1909  return false;
1910  });
1911 
1912  // Do not enqueue if redundant.
1913  if (currentIsRedundant)
1914  return true;
1915 
1916  aTree.put("rectangle", aNewRect.toString().getStr());
1917  aCallbackData.setJson(aTree);
1918  assert(aCallbackData.validate() && "Validation after setJson failed!");
1919  }
1920  }
1921  else if (aAction == "created")
1922  {
1923  // Remove all previous actions on same dialog, if we are creating it anew.
1924  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1925  if (elem.Type == LOK_CALLBACK_WINDOW)
1926  {
1927  const boost::property_tree::ptree& aOldTree = elem.getJson();
1928  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1929  return true;
1930  }
1931  return false;
1932  });
1933 
1934  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
1935  if (!pWindow)
1936  {
1937  gImpl->maLastExceptionMsg = "Document doesn't support dialog rendering, or window not found.";
1938  return false;
1939  }
1940 
1941 #ifndef IOS
1942  auto xClip = forceSetClipboardForCurrentView(m_pDocument);
1943 
1944  uno::Reference<datatransfer::clipboard::XClipboard> xClipboard(xClip);
1945  pWindow->SetClipboard(xClipboard);
1946 #endif
1947  }
1948  else if (aAction == "size_changed")
1949  {
1950  // A size change is practically re-creation of the window.
1951  // But at a minimum it's a full invalidation.
1952  removeAll([&nLOKWindowId](const queue_type::value_type& elem) {
1953  if (elem.Type == LOK_CALLBACK_WINDOW)
1954  {
1955  const boost::property_tree::ptree& aOldTree = elem.getJson();
1956  if (nLOKWindowId == aOldTree.get<unsigned>("id", 0))
1957  {
1958  const std::string aOldAction = aOldTree.get<std::string>("action", "");
1959  if (aOldAction == "invalidate")
1960  return true;
1961  }
1962  }
1963  return false;
1964  });
1965  }
1966 
1967  // Queue this one.
1968  return false;
1969 }
1970 
1971 void CallbackFlushHandler::Invoke()
1972 {
1973  comphelper::ProfileZone aZone("CallbackFlushHandler::Invoke");
1974 
1975  if (!m_pCallback)
1976  return;
1977 
1978  std::scoped_lock<std::mutex> lock(m_mutex);
1979 
1980  SAL_INFO("lok", "Flushing " << m_queue.size() << " elements.");
1981  for (const auto& rCallbackData : m_queue)
1982  {
1983  const int type = rCallbackData.Type;
1984  const auto& payload = rCallbackData.PayloadString;
1985  const int viewId = lcl_isViewCallbackType(type) ? lcl_getViewId(rCallbackData) : -1;
1986 
1987  if (viewId == -1)
1988  {
1989  const auto stateIt = m_states.find(type);
1990  if (stateIt != m_states.end())
1991  {
1992  // If the state didn't change, it's safe to ignore.
1993  if (stateIt->second == payload)
1994  {
1995  SAL_INFO("lok", "Skipping duplicate [" << type << "]: [" << payload << "].");
1996  continue;
1997  }
1998 
1999  stateIt->second = payload;
2000  }
2001  }
2002  else
2003  {
2004  const auto statesIt = m_viewStates.find(viewId);
2005  if (statesIt != m_viewStates.end())
2006  {
2007  auto& states = statesIt->second;
2008  const auto stateIt = states.find(type);
2009  if (stateIt != states.end())
2010  {
2011  // If the state didn't change, it's safe to ignore.
2012  if (stateIt->second == payload)
2013  {
2014  SAL_INFO("lok", "Skipping view duplicate [" << type << ',' << viewId << "]: [" << payload << "].");
2015  continue;
2016  }
2017 
2018  SAL_INFO("lok", "Replacing an element in view states [" << type << ',' << viewId << "]: [" << payload << "].");
2019  stateIt->second = payload;
2020  }
2021  else
2022  {
2023  SAL_INFO("lok", "Inserted a new element in view states: [" << type << ',' << viewId << "]: [" << payload << "]");
2024  states.emplace(type, payload);
2025 
2026  }
2027  }
2028  }
2029 
2030  m_pCallback(type, payload.c_str(), m_pData);
2031  }
2032 
2033  m_queue.clear();
2034 }
2035 
2036 bool CallbackFlushHandler::removeAll(const std::function<bool (const CallbackFlushHandler::queue_type::value_type&)>& rTestFunc)
2037 {
2038  auto newEnd = std::remove_if(m_queue.begin(), m_queue.end(), rTestFunc);
2039  if (newEnd != m_queue.end())
2040  {
2041  m_queue.erase(newEnd, m_queue.end());
2042  return true;
2043  }
2044 
2045  return false;
2046 }
2047 
2049 {
2050  const auto& result = m_viewStates.emplace(viewId, decltype(m_viewStates)::mapped_type());
2051  if (!result.second && result.first != m_viewStates.end())
2052  {
2053  result.first->second.clear();
2054  }
2055 }
2056 
2058 {
2059  m_viewStates.erase(viewId);
2060 }
2061 
2062 
2063 static void doc_destroy(LibreOfficeKitDocument *pThis)
2064 {
2065  comphelper::ProfileZone aZone("doc_destroy");
2066 
2067  SolarMutexGuard aGuard;
2068 
2070 
2071  LibLODocument_Impl *pDocument = static_cast<LibLODocument_Impl*>(pThis);
2072  delete pDocument;
2073 }
2074 
2075 static void lo_destroy (LibreOfficeKit* pThis);
2076 static int lo_initialize (LibreOfficeKit* pThis, const char* pInstallPath, const char* pUserProfilePath);
2077 static LibreOfficeKitDocument* lo_documentLoad (LibreOfficeKit* pThis, const char* pURL);
2078 static char * lo_getError (LibreOfficeKit* pThis);
2079 static void lo_freeError (char* pFree);
2080 static LibreOfficeKitDocument* lo_documentLoadWithOptions (LibreOfficeKit* pThis,
2081  const char* pURL,
2082  const char* pOptions);
2083 static void lo_registerCallback (LibreOfficeKit* pThis,
2084  LibreOfficeKitCallback pCallback,
2085  void* pData);
2086 static char* lo_getFilterTypes(LibreOfficeKit* pThis);
2087 static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long features);
2088 static void lo_setDocumentPassword(LibreOfficeKit* pThis,
2089  const char* pURL,
2090  const char* pPassword);
2091 static char* lo_getVersionInfo(LibreOfficeKit* pThis);
2092 static int lo_runMacro (LibreOfficeKit* pThis, const char* pURL);
2093 
2094 static bool lo_signDocument(LibreOfficeKit* pThis,
2095  const char* pUrl,
2096  const unsigned char* pCertificateBinary,
2097  const int nCertificateBinarySize,
2098  const unsigned char* pPrivateKeyBinary,
2099  const int nPrivateKeyBinarySize);
2100 
2101 static void lo_runLoop(LibreOfficeKit* pThis,
2102  LibreOfficeKitPollCallback pPollCallback,
2103  LibreOfficeKitWakeCallback pWakeCallback,
2104  void* pData);
2105 
2106 static void lo_sendDialogEvent(LibreOfficeKit* pThis,
2107  unsigned long long int nLOKWindowId,
2108  const char* pArguments);
2109 
2110 static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const char* pValue);
2111 
2113  : m_pOfficeClass( gOfficeClass.lock() )
2114  , maThread(nullptr)
2115  , mpCallback(nullptr)
2116  , mpCallbackData(nullptr)
2117  , mOptionalFeatures(0)
2118 {
2119  if(!m_pOfficeClass) {
2120  m_pOfficeClass = std::make_shared<LibreOfficeKitClass>();
2121  m_pOfficeClass->nSize = sizeof(LibreOfficeKitClass);
2122 
2123  m_pOfficeClass->destroy = lo_destroy;
2124  m_pOfficeClass->documentLoad = lo_documentLoad;
2125  m_pOfficeClass->getError = lo_getError;
2126  m_pOfficeClass->freeError = lo_freeError;
2127  m_pOfficeClass->documentLoadWithOptions = lo_documentLoadWithOptions;
2128  m_pOfficeClass->registerCallback = lo_registerCallback;
2129  m_pOfficeClass->getFilterTypes = lo_getFilterTypes;
2130  m_pOfficeClass->setOptionalFeatures = lo_setOptionalFeatures;
2131  m_pOfficeClass->setDocumentPassword = lo_setDocumentPassword;
2132  m_pOfficeClass->getVersionInfo = lo_getVersionInfo;
2133  m_pOfficeClass->runMacro = lo_runMacro;
2134  m_pOfficeClass->signDocument = lo_signDocument;
2135  m_pOfficeClass->runLoop = lo_runLoop;
2136  m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent;
2137  m_pOfficeClass->setOption = lo_setOption;
2138 
2140  }
2141 
2142  pClass = m_pOfficeClass.get();
2143 }
2144 
2146 {
2147 }
2148 
2149 namespace
2150 {
2151 
2152 #ifdef IOS
2153 void paintTileToCGContext(ITiledRenderable* pDocument,
2154  void* rCGContext, const Size nCanvasSize,
2155  const int nTilePosX, const int nTilePosY,
2156  const int nTileWidth, const int nTileHeight)
2157 {
2159  aData.rCGContext = reinterpret_cast<CGContextRef>(rCGContext);
2160 
2161  ScopedVclPtrInstance<VirtualDevice> pDevice(aData, Size(1, 1), DeviceFormat::DEFAULT);
2162  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
2163  pDevice->SetOutputSizePixel(nCanvasSize);
2164  pDocument->paintTile(*pDevice, nCanvasSize.Width(), nCanvasSize.Height(),
2165  nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2166 }
2167 
2168 void paintTileIOS(LibreOfficeKitDocument* pThis,
2169  unsigned char* pBuffer,
2170  const int nCanvasWidth, const int nCanvasHeight, const double fDPIScale,
2171  const int nTilePosX, const int nTilePosY,
2172  const int nTileWidth, const int nTileHeight)
2173 {
2174  CGContextRef pCGContext = CGBitmapContextCreate(pBuffer, nCanvasWidth, nCanvasHeight, 8,
2175  nCanvasWidth * 4, CGColorSpaceCreateDeviceRGB(),
2176  kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little);
2177 
2178  CGContextTranslateCTM(pCGContext, 0, nCanvasHeight);
2179  CGContextScaleCTM(pCGContext, fDPIScale, -fDPIScale);
2180 
2181  doc_paintTileToCGContext(pThis, (void*) pCGContext, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
2182 
2183  CGContextRelease(pCGContext);
2184 }
2185 #endif
2186 
2187 void setLanguageAndLocale(OUString const & aLangISO)
2188 {
2189  SvtSysLocaleOptions aLocalOptions;
2190  aLocalOptions.SetLocaleConfigString(aLangISO);
2191  aLocalOptions.SetUILocaleConfigString(aLangISO);
2192  aLocalOptions.Commit();
2193 }
2194 
2195 void setFormatSpecificFilterData(std::u16string_view sFormat, comphelper::SequenceAsHashMap & rFilterDataMap)
2196 {
2197  if (sFormat == u"pdf")
2198  {
2199  // always export bookmarks, which is needed for annotations
2200  rFilterDataMap["ExportBookmarks"] <<= true;
2201  }
2202 }
2203 
2204 } // anonymous namespace
2205 
2206 // Wonder global state ...
2207 static uno::Reference<css::uno::XComponentContext> xContext;
2208 static uno::Reference<css::lang::XMultiServiceFactory> xSFactory;
2209 static uno::Reference<css::lang::XMultiComponentFactory> xFactory;
2210 
2211 static LibreOfficeKitDocument* lo_documentLoad(LibreOfficeKit* pThis, const char* pURL)
2212 {
2213  return lo_documentLoadWithOptions(pThis, pURL, nullptr);
2214 }
2215 
2216 static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis, const char* pURL, const char* pOptions)
2217 {
2218  comphelper::ProfileZone aZone("lo_documentLoadWithOptions");
2219 
2220  SolarMutexGuard aGuard;
2221 
2222  static int nDocumentIdCounter = 0;
2223 
2224  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2225  pLib->maLastExceptionMsg.clear();
2226 
2227  const OUString aURL(getAbsoluteURL(pURL));
2228  if (aURL.isEmpty())
2229  {
2230  pLib->maLastExceptionMsg = "Filename to load was not provided.";
2231  SAL_INFO("lok", "URL for load is empty");
2232  return nullptr;
2233  }
2234 
2235  pLib->maLastExceptionMsg.clear();
2236 
2237  if (!xContext.is())
2238  {
2239  pLib->maLastExceptionMsg = "ComponentContext is not available";
2240  SAL_INFO("lok", "ComponentContext is not available");
2241  return nullptr;
2242  }
2243 
2244  uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2245 
2246  if (!xComponentLoader.is())
2247  {
2248  pLib->maLastExceptionMsg = "ComponentLoader is not available";
2249  SAL_INFO("lok", "ComponentLoader is not available");
2250  return nullptr;
2251  }
2252 
2253  try
2254  {
2255  // 'Language=...' is an option that LOK consumes by itself, and does
2256  // not pass it as a parameter to the filter
2257  OUString aOptions = getUString(pOptions);
2258  const OUString aLanguage = extractParameter(aOptions, u"Language");
2259  bool isValidLangTag = LanguageTag::isValidBcp47(aLanguage, nullptr);
2260 
2261  if (!aLanguage.isEmpty() && isValidLangTag)
2262  {
2264  // Set the LOK language tag, used for dialog tunneling.
2267 
2268  SAL_INFO("lok", "Set document language to " << aLanguage);
2269  // use with care - it sets it for the entire core, not just the
2270  // document
2271  setLanguageAndLocale(aLanguage);
2272  // Need to reset the static initialized values
2274  }
2275 
2276  const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
2277  SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
2278 
2279  const OUString aBatch = extractParameter(aOptions, u"Batch");
2280  if (!aBatch.isEmpty())
2281  {
2282  Application::SetDialogCancelMode(DialogCancelMode::LOKSilent);
2283  }
2284 
2285  uno::Sequence<css::beans::PropertyValue> aFilterOptions(3);
2286  aFilterOptions[0] = css::beans::PropertyValue( "FilterOptions",
2287  0,
2288  uno::makeAny(aOptions),
2289  beans::PropertyState_DIRECT_VALUE);
2290 
2291  rtl::Reference<LOKInteractionHandler> const pInteraction(
2292  new LOKInteractionHandler("load", pLib));
2293  auto const pair(pLib->mInteractionMap.insert(std::make_pair(aURL.toUtf8(), pInteraction)));
2294  comphelper::ScopeGuard const g([&] () {
2295  if (pair.second)
2296  {
2297  pLib->mInteractionMap.erase(aURL.toUtf8());
2298  }
2299  });
2300  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
2301  aFilterOptions[1].Name = "InteractionHandler";
2302  aFilterOptions[1].Value <<= xInteraction;
2303 
2304  int nMacroSecurityLevel = 1;
2305  const OUString aMacroSecurityLevel = extractParameter(aOptions, u"MacroSecurityLevel");
2306  if (!aMacroSecurityLevel.isEmpty())
2307  {
2308  double nNumber;
2309  sal_uInt32 nFormat = 1;
2311  if (aFormatter.IsNumberFormat(aMacroSecurityLevel, nFormat, nNumber))
2312  nMacroSecurityLevel = static_cast<int>(nNumber);
2313  }
2314  SvtSecurityOptions().SetMacroSecurityLevel(nMacroSecurityLevel);
2315 
2316 #if defined(ANDROID) && HAVE_FEATURE_ANDROID_LOK
2317  sal_Int16 nMacroExecMode = document::MacroExecMode::USE_CONFIG;
2318 #else
2319  const OUString aEnableMacrosExecution = extractParameter(aOptions, u"EnableMacrosExecution");
2320  sal_Int16 nMacroExecMode = aEnableMacrosExecution == "true" ? document::MacroExecMode::USE_CONFIG :
2321  document::MacroExecMode::NEVER_EXECUTE;
2322 #endif
2323  aFilterOptions[2].Name = "MacroExecutionMode";
2324  aFilterOptions[2].Value <<= nMacroExecMode;
2325 
2326  /* TODO
2327  sal_Int16 nUpdateDoc = document::UpdateDocMode::ACCORDING_TO_CONFIG;
2328  aFilterOptions[3].Name = "UpdateDocMode";
2329  aFilterOptions[3].Value <<= nUpdateDoc;
2330  */
2331 
2332  const int nThisDocumentId = nDocumentIdCounter++;
2334  uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
2335  aURL, "_blank", 0,
2336  aFilterOptions);
2337 
2338  assert(!xComponent.is() || pair.second); // concurrent loading of same URL ought to fail
2339 
2340  if (!xComponent.is())
2341  {
2342  pLib->maLastExceptionMsg = "loadComponentFromURL returned an empty reference";
2343  SAL_INFO("lok", "Document can't be loaded - " << pLib->maLastExceptionMsg);
2344  return nullptr;
2345  }
2346 
2347  LibLODocument_Impl* pDocument = new LibLODocument_Impl(xComponent, nThisDocumentId);
2348 
2349  // After loading the document, its initial view is the "current" view.
2350  if (pLib->mpCallback)
2351  {
2352  int nState = doc_getSignatureState(pDocument);
2353  pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
2354  }
2355  return pDocument;
2356  }
2357  catch (const uno::Exception& exception)
2358  {
2359  pLib->maLastExceptionMsg = exception.Message;
2360  TOOLS_INFO_EXCEPTION("lok", "Document can't be loaded");
2361  }
2362 
2363  return nullptr;
2364 }
2365 
2366 static int lo_runMacro(LibreOfficeKit* pThis, const char *pURL)
2367 {
2368  comphelper::ProfileZone aZone("lo_runMacro");
2369 
2370  SolarMutexGuard aGuard;
2371 
2372  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2373  pLib->maLastExceptionMsg.clear();
2374 
2375  OUString sURL( pURL, strlen(pURL), RTL_TEXTENCODING_UTF8 );
2376  if (sURL.isEmpty())
2377  {
2378  pLib->maLastExceptionMsg = "Macro to run was not provided.";
2379  SAL_INFO("lok", "Macro URL is empty");
2380  return false;
2381  }
2382 
2383  if (!sURL.startsWith("macro://"))
2384  {
2385  pLib->maLastExceptionMsg = "This doesn't look like macro URL";
2386  SAL_INFO("lok", "Macro URL is invalid");
2387  return false;
2388  }
2389 
2390  pLib->maLastExceptionMsg.clear();
2391 
2392  if (!xContext.is())
2393  {
2394  pLib->maLastExceptionMsg = "ComponentContext is not available";
2395  SAL_INFO("lok", "ComponentContext is not available");
2396  return false;
2397  }
2398 
2399  util::URL aURL;
2400  aURL.Complete = sURL;
2401 
2402  uno::Reference < util::XURLTransformer > xParser( util::URLTransformer::create( xContext ) );
2403 
2404  if( xParser.is() )
2405  xParser->parseStrict( aURL );
2406 
2407  uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
2408 
2409  if (!xComponentLoader.is())
2410  {
2411  pLib->maLastExceptionMsg = "ComponentLoader is not available";
2412  SAL_INFO("lok", "ComponentLoader is not available");
2413  return false;
2414  }
2415 
2416  xFactory = xContext->getServiceManager();
2417 
2418  if (xFactory.is())
2419  {
2420  uno::Reference<frame::XDispatchProvider> xDP;
2421  xSFactory.set(xFactory, uno::UNO_QUERY_THROW);
2422  xDP.set( xSFactory->createInstance("com.sun.star.comp.sfx2.SfxMacroLoader"), uno::UNO_QUERY );
2423  uno::Reference<frame::XDispatch> xD = xDP->queryDispatch( aURL, OUString(), 0);
2424 
2425  if (!xD.is())
2426  {
2427  pLib->maLastExceptionMsg = "Macro loader is not available";
2428  SAL_INFO("lok", "Macro loader is not available");
2429  return false;
2430  }
2431 
2432  uno::Reference < frame::XSynchronousDispatch > xSyncDisp( xD, uno::UNO_QUERY_THROW );
2433  uno::Sequence<css::beans::PropertyValue> aEmpty;
2434  css::beans::PropertyValue aErr;
2435  uno::Any aRet = xSyncDisp->dispatchWithReturnValue( aURL, aEmpty );
2436  aRet >>= aErr;
2437 
2438  if (aErr.Name == "ErrorCode")
2439  {
2440  sal_uInt32 nErrCode = 0; // ERRCODE_NONE
2441  aErr.Value >>= nErrCode;
2442 
2443  pLib->maLastExceptionMsg = "An error occurred running macro (error code: " + OUString::number( nErrCode ) + ")";
2444  SAL_INFO("lok", "Macro execution terminated with error code " << nErrCode);
2445 
2446  return false;
2447  }
2448 
2449  return true;
2450  }
2451 
2452  return false;
2453 }
2454 
2455 static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
2456  const char* pURL,
2457  const unsigned char* pCertificateBinary,
2458  const int nCertificateBinarySize,
2459  const unsigned char* pPrivateKeyBinary,
2460  const int nPrivateKeyBinarySize)
2461 {
2462  comphelper::ProfileZone aZone("lo_signDocument");
2463 
2464  OUString aURL(getAbsoluteURL(pURL));
2465  if (aURL.isEmpty())
2466  return false;
2467 
2468  if (!xContext.is())
2469  return false;
2470 
2471  uno::Sequence<sal_Int8> aCertificateSequence;
2472 
2473  std::string aCertificateString(reinterpret_cast<const char*>(pCertificateBinary), nCertificateBinarySize);
2474  std::string aCertificateBase64String = extractCertificate(aCertificateString);
2475  if (!aCertificateBase64String.empty())
2476  {
2477  OUString aBase64OUString = OUString::createFromAscii(aCertificateBase64String.c_str());
2478  comphelper::Base64::decode(aCertificateSequence, aBase64OUString);
2479  }
2480  else
2481  {
2482  aCertificateSequence.realloc(nCertificateBinarySize);
2483  std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
2484  }
2485 
2486  uno::Sequence<sal_Int8> aPrivateKeySequence;
2487  std::string aPrivateKeyString(reinterpret_cast<const char*>(pPrivateKeyBinary), nPrivateKeyBinarySize);
2488  std::string aPrivateKeyBase64String = extractPrivateKey(aPrivateKeyString);
2489  if (!aPrivateKeyBase64String.empty())
2490  {
2491  OUString aBase64OUString = OUString::createFromAscii(aPrivateKeyBase64String.c_str());
2492  comphelper::Base64::decode(aPrivateKeySequence, aBase64OUString);
2493  }
2494  else
2495  {
2496  aPrivateKeySequence.realloc(nPrivateKeyBinarySize);
2497  std::copy(pPrivateKeyBinary, pPrivateKeyBinary + nPrivateKeyBinarySize, aPrivateKeySequence.begin());
2498  }
2499 
2500  uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
2501  uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
2502  if (!xSecurityContext.is())
2503  return false;
2504 
2505  uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
2506  uno::Reference<xml::crypto::XCertificateCreator> xCertificateCreator(xSecurityEnvironment, uno::UNO_QUERY);
2507 
2508  if (!xCertificateCreator.is())
2509  return false;
2510 
2511  uno::Reference<security::XCertificate> xCertificate = xCertificateCreator->createDERCertificateWithPrivateKey(aCertificateSequence, aPrivateKeySequence);
2512 
2513  if (!xCertificate.is())
2514  return false;
2515 
2516  sfx2::DocumentSigner aDocumentSigner(aURL);
2517  if (!aDocumentSigner.signDocument(xCertificate))
2518  return false;
2519 
2520  return true;
2521 }
2522 
2523 static void lo_registerCallback (LibreOfficeKit* pThis,
2524  LibreOfficeKitCallback pCallback,
2525  void* pData)
2526 {
2527  SolarMutexGuard aGuard;
2528 
2529  Application* pApp = GetpApp();
2530  assert(pApp);
2531 
2532  LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
2533  pLib->maLastExceptionMsg.clear();
2534 
2535  pApp->m_pCallback = pLib->mpCallback = pCallback;
2536  pApp->m_pCallbackData = pLib->mpCallbackData = pData;
2537 }
2538 
2539 static int doc_saveAs(LibreOfficeKitDocument* pThis, const char* sUrl, const char* pFormat, const char* pFilterOptions)
2540 {
2541  comphelper::ProfileZone aZone("doc_saveAs");
2542 
2543  SolarMutexGuard aGuard;
2545 
2546  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2547 
2548  OUString sFormat = getUString(pFormat);
2549  OUString aURL(getAbsoluteURL(sUrl));
2550 
2551  uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2552 
2553  if (aURL.isEmpty())
2554  {
2555  SetLastExceptionMsg("Filename to save to was not provided.");
2556  SAL_INFO("lok", "URL for save is empty");
2557  return false;
2558  }
2559 
2560  try
2561  {
2562  const ExtensionMap* pMap;
2563 
2564  switch (doc_getDocumentType(pThis))
2565  {
2566  case LOK_DOCTYPE_SPREADSHEET:
2567  pMap = aCalcExtensionMap;
2568  break;
2569  case LOK_DOCTYPE_PRESENTATION:
2570  pMap = aImpressExtensionMap;
2571  break;
2572  case LOK_DOCTYPE_DRAWING:
2573  pMap = aDrawExtensionMap;
2574  break;
2575  case LOK_DOCTYPE_TEXT:
2576  pMap = aWriterExtensionMap;
2577  break;
2578  case LOK_DOCTYPE_OTHER:
2579  default:
2580  SAL_INFO("lok", "Can't save document - unsupported document type.");
2581  return false;
2582  }
2583 
2584  if (pFormat == nullptr)
2585  {
2586  // sniff from the extension
2587  sal_Int32 idx = aURL.lastIndexOf(".");
2588  if( idx > 0 )
2589  {
2590  sFormat = aURL.copy( idx + 1 );
2591  }
2592  else
2593  {
2594  SetLastExceptionMsg("input filename without a suffix");
2595  return false;
2596  }
2597  }
2598 
2599  OUString aFilterName;
2600  for (sal_Int32 i = 0; pMap[i].extn; ++i)
2601  {
2602  if (sFormat.equalsIgnoreAsciiCaseAscii(pMap[i].extn))
2603  {
2604  aFilterName = getUString(pMap[i].filterName);
2605  break;
2606  }
2607  }
2608  if (aFilterName.isEmpty())
2609  {
2610  SetLastExceptionMsg("no output filter found for provided suffix");
2611  return false;
2612  }
2613 
2614  OUString aFilterOptions = getUString(pFilterOptions);
2615 
2616  // Check if watermark for pdf is passed by filteroptions...
2617  // It is not a real filter option so it must be filtered out.
2618  OUString watermarkText, sFullSheetPreview;
2619  int aIndex = -1;
2620  if ((aIndex = aFilterOptions.indexOf(",Watermark=")) >= 0)
2621  {
2622  int bIndex = aFilterOptions.indexOf("WATERMARKEND");
2623  watermarkText = aFilterOptions.subView(aIndex+11, bIndex-(aIndex+11));
2624  aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+12);
2625  }
2626 
2627  if ((aIndex = aFilterOptions.indexOf(",FullSheetPreview=")) >= 0)
2628  {
2629  int bIndex = aFilterOptions.indexOf("FULLSHEETPREVEND");
2630  sFullSheetPreview = aFilterOptions.subView(aIndex+18, bIndex-(aIndex+18));
2631  aFilterOptions = OUString::Concat(aFilterOptions.subView(0, aIndex)) + aFilterOptions.subView(bIndex+16);
2632  }
2633 
2634  bool bFullSheetPreview = sFullSheetPreview == "true";
2635 
2636  // 'TakeOwnership' == this is a 'real' SaveAs (that is, the document
2637  // gets a new name). When this is not provided, the meaning of
2638  // saveAs() is more like save-a-copy, which allows saving to any
2639  // random format like PDF or PNG.
2640  // It is not a real filter option, so we have to filter it out.
2641  const uno::Sequence<OUString> aOptionSeq = comphelper::string::convertCommaSeparated(aFilterOptions);
2642  std::vector<OUString> aFilteredOptionVec;
2643  bool bTakeOwnership = false;
2644  MediaDescriptor aSaveMediaDescriptor;
2645  for (const auto& rOption : aOptionSeq)
2646  {
2647  if (rOption == "TakeOwnership")
2648  bTakeOwnership = true;
2649  else if (rOption == "NoFileSync")
2650  aSaveMediaDescriptor["NoFileSync"] <<= true;
2651  else
2652  aFilteredOptionVec.push_back(rOption);
2653  }
2654 
2655  aSaveMediaDescriptor["Overwrite"] <<= true;
2656  aSaveMediaDescriptor["FilterName"] <<= aFilterName;
2657 
2658  auto aFilteredOptionSeq = comphelper::containerToSequence<OUString>(aFilteredOptionVec);
2659  aFilterOptions = comphelper::string::convertCommaSeparated(aFilteredOptionSeq);
2660  aSaveMediaDescriptor[MediaDescriptor::PROP_FILTEROPTIONS()] <<= aFilterOptions;
2661 
2662  comphelper::SequenceAsHashMap aFilterDataMap;
2663 
2664  setFormatSpecificFilterData(sFormat, aFilterDataMap);
2665 
2666  if (!watermarkText.isEmpty())
2667  aFilterDataMap["TiledWatermark"] <<= watermarkText;
2668 
2669  if (bFullSheetPreview)
2670  aFilterDataMap["SinglePageSheets"] <<= true;
2671 
2672  if (!aFilterDataMap.empty())
2673  {
2674  aSaveMediaDescriptor["FilterData"] <<= aFilterDataMap.getAsConstPropertyValueList();
2675  }
2676 
2677  // add interaction handler too
2678  if (gImpl)
2679  {
2680  // gImpl does not have to exist when running from a unit test
2681  rtl::Reference<LOKInteractionHandler> const pInteraction(
2682  new LOKInteractionHandler("saveas", gImpl, pDocument));
2683  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
2684 
2685  aSaveMediaDescriptor[MediaDescriptor::PROP_INTERACTIONHANDLER()] <<= xInteraction;
2686  }
2687 
2688 
2689  if (bTakeOwnership)
2690  xStorable->storeAsURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2691  else
2692  xStorable->storeToURL(aURL, aSaveMediaDescriptor.getAsConstPropertyValueList());
2693 
2694  return true;
2695  }
2696  catch (const uno::Exception& exception)
2697  {
2698  SetLastExceptionMsg("exception: " + exception.Message);
2699  }
2700  return false;
2701 }
2702 
2709 static void doc_iniUnoCommands ()
2710 {
2711  SolarMutexGuard aGuard;
2713 
2714  OUString sUnoCommands[] =
2715  {
2716  OUString(".uno:AlignLeft"),
2717  OUString(".uno:AlignHorizontalCenter"),
2718  OUString(".uno:AlignRight"),
2719  OUString(".uno:BackColor"),
2720  OUString(".uno:BackgroundColor"),
2721  OUString(".uno:TableCellBackgroundColor"),
2722  OUString(".uno:Bold"),
2723  OUString(".uno:CenterPara"),
2724  OUString(".uno:CharBackColor"),
2725  OUString(".uno:CharBackgroundExt"),
2726  OUString(".uno:CharFontName"),
2727  OUString(".uno:Color"),
2728  OUString(".uno:ControlCodes"),
2729  OUString(".uno:DecrementIndent"),
2730  OUString(".uno:DefaultBullet"),
2731  OUString(".uno:DefaultNumbering"),
2732  OUString(".uno:FontColor"),
2733  OUString(".uno:FontHeight"),
2734  OUString(".uno:IncrementIndent"),
2735  OUString(".uno:Italic"),
2736  OUString(".uno:JustifyPara"),
2737  OUString(".uno:OutlineFont"),
2738  OUString(".uno:LeftPara"),
2739  OUString(".uno:LanguageStatus"),
2740  OUString(".uno:RightPara"),
2741  OUString(".uno:Shadowed"),
2742  OUString(".uno:SubScript"),
2743  OUString(".uno:SuperScript"),
2744  OUString(".uno:Strikeout"),
2745  OUString(".uno:StyleApply"),
2746  OUString(".uno:Underline"),
2747  OUString(".uno:ModifiedStatus"),
2748  OUString(".uno:Undo"),
2749  OUString(".uno:Redo"),
2750  OUString(".uno:InsertPage"),
2751  OUString(".uno:DeletePage"),
2752  OUString(".uno:DuplicatePage"),
2753  OUString(".uno:Cut"),
2754  OUString(".uno:Copy"),
2755  OUString(".uno:Paste"),
2756  OUString(".uno:SelectAll"),
2757  OUString(".uno:InsertAnnotation"),
2758  OUString(".uno:DeleteAnnotation"),
2759  OUString(".uno:ReplyComment"),
2760  OUString(".uno:ResolveComment"),
2761  OUString(".uno:ResolveCommentThread"),
2762  OUString(".uno:InsertRowsBefore"),
2763  OUString(".uno:InsertRowsAfter"),
2764  OUString(".uno:InsertColumnsBefore"),
2765  OUString(".uno:InsertColumnsAfter"),
2766  OUString(".uno:MergeCells"),
2767  OUString(".uno:DeleteRows"),
2768  OUString(".uno:DeleteColumns"),
2769  OUString(".uno:DeleteTable"),
2770  OUString(".uno:SelectTable"),
2771  OUString(".uno:EntireRow"),
2772  OUString(".uno:EntireColumn"),
2773  OUString(".uno:EntireCell"),
2774  OUString(".uno:AssignLayout"),
2775  OUString(".uno:StatusDocPos"),
2776  OUString(".uno:RowColSelCount"),
2777  OUString(".uno:StatusPageStyle"),
2778  OUString(".uno:InsertMode"),
2779  OUString(".uno:SpellOnline"),
2780  OUString(".uno:StatusSelectionMode"),
2781  OUString(".uno:StateTableCell"),
2782  OUString(".uno:StatusBarFunc"),
2783  OUString(".uno:StatePageNumber"),
2784  OUString(".uno:StateWordCount"),
2785  OUString(".uno:SelectionMode"),
2786  OUString(".uno:PageStatus"),
2787  OUString(".uno:LayoutStatus"),
2788  OUString(".uno:Scale"),
2789  OUString(".uno:Context"),
2790  OUString(".uno:WrapText"),
2791  OUString(".uno:ToggleMergeCells"),
2792  OUString(".uno:NumberFormatCurrency"),
2793  OUString(".uno:NumberFormatPercent"),
2794  OUString(".uno:NumberFormatDecimal"),
2795  OUString(".uno:NumberFormatDate"),
2796  OUString(".uno:FrameLineColor"),
2797  OUString(".uno:SortAscending"),
2798  OUString(".uno:SortDescending"),
2799  OUString(".uno:TrackChanges"),
2800  OUString(".uno:ShowTrackedChanges"),
2801  OUString(".uno:NextTrackedChange"),
2802  OUString(".uno:PreviousTrackedChange"),
2803  OUString(".uno:AcceptAllTrackedChanges"),
2804  OUString(".uno:RejectAllTrackedChanges"),
2805  OUString(".uno:TableDialog"),
2806  OUString(".uno:FormatCellDialog"),
2807  OUString(".uno:FontDialog"),
2808  OUString(".uno:ParagraphDialog"),
2809  OUString(".uno:OutlineBullet"),
2810  OUString(".uno:InsertIndexesEntry"),
2811  OUString(".uno:DocumentRepair"),
2812  OUString(".uno:TransformDialog"),
2813  OUString(".uno:InsertPageHeader"),
2814  OUString(".uno:InsertPageFooter"),
2815  OUString(".uno:OnlineAutoFormat"),
2816  OUString(".uno:InsertObjectChart"),
2817  OUString(".uno:InsertSection"),
2818  OUString(".uno:InsertAnnotation"),
2819  OUString(".uno:InsertPagebreak"),
2820  OUString(".uno:InsertColumnBreak"),
2821  OUString(".uno:HyperlinkDialog"),
2822  OUString(".uno:InsertSymbol"),
2823  OUString(".uno:EditRegion"),
2824  OUString(".uno:ThesaurusDialog"),
2825  OUString(".uno:FormatArea"),
2826  OUString(".uno:FormatLine"),
2827  OUString(".uno:FormatColumns"),
2828  OUString(".uno:Watermark"),
2829  OUString(".uno:ResetAttributes"),
2830  OUString(".uno:Orientation"),
2831  OUString(".uno:ObjectAlignLeft"),
2832  OUString(".uno:ObjectAlignRight"),
2833  OUString(".uno:AlignCenter"),
2834  OUString(".uno:TransformPosX"),
2835  OUString(".uno:TransformPosY"),
2836  OUString(".uno:TransformWidth"),
2837  OUString(".uno:TransformHeight"),
2838  OUString(".uno:ObjectBackOne"),
2839  OUString(".uno:SendToBack"),
2840  OUString(".uno:ObjectForwardOne"),
2841  OUString(".uno:BringToFront"),
2842  OUString(".uno:WrapRight"),
2843  OUString(".uno:WrapThrough"),
2844  OUString(".uno:WrapLeft"),
2845  OUString(".uno:WrapIdeal"),
2846  OUString(".uno:WrapOn"),
2847  OUString(".uno:WrapOff"),
2848  OUString(".uno:UpdateCurIndex"),
2849  OUString(".uno:InsertCaptionDialog"),
2850  OUString(".uno:FormatGroup"),
2851  OUString(".uno:SplitTable"),
2852  OUString(".uno:MergeCells"),
2853  OUString(".uno:DeleteNote"),
2854  OUString(".uno:AcceptChanges"),
2855  OUString(".uno:FormatPaintbrush"),
2856  OUString(".uno:SetDefault"),
2857  OUString(".uno:ParaLeftToRight"),
2858  OUString(".uno:ParaRightToLeft"),
2859  OUString(".uno:ParaspaceIncrease"),
2860  OUString(".uno:ParaspaceDecrease"),
2861  OUString(".uno:AcceptTrackedChange"),
2862  OUString(".uno:RejectTrackedChange"),
2863  OUString(".uno:ShowResolvedAnnotations"),
2864  OUString(".uno:InsertBreak"),
2865  OUString(".uno:InsertEndnote"),
2866  OUString(".uno:InsertFootnote"),
2867  OUString(".uno:InsertReferenceField"),
2868  OUString(".uno:InsertBookmark"),
2869  OUString(".uno:InsertAuthoritiesEntry"),
2870  OUString(".uno:InsertMultiIndex"),
2871  OUString(".uno:InsertField"),
2872  OUString(".uno:InsertPageNumberField"),
2873  OUString(".uno:InsertPageCountField"),
2874  OUString(".uno:InsertDateField"),
2875  OUString(".uno:InsertTitleField"),
2876  OUString(".uno:InsertFieldCtrl"),
2877  OUString(".uno:CharmapControl"),
2878  OUString(".uno:EnterGroup"),
2879  OUString(".uno:LeaveGroup"),
2880  OUString(".uno:AlignUp"),
2881  OUString(".uno:AlignMiddle"),
2882  OUString(".uno:AlignDown"),
2883  OUString(".uno:TraceChangeMode"),
2884  OUString(".uno:Combine"),
2885  OUString(".uno:Merge"),
2886  OUString(".uno:Dismantle"),
2887  OUString(".uno:Substract"),
2888  OUString(".uno:DistributeSelection"),
2889  OUString(".uno:Intersect"),
2890  OUString(".uno:BorderInner"),
2891  OUString(".uno:BorderOuter"),
2892  OUString(".uno:FreezePanes"),
2893  OUString(".uno:FreezePanesColumn"),
2894  OUString(".uno:FreezePanesRow"),
2895  OUString(".uno:Sidebar"),
2896  OUString(".uno:SheetRightToLeft"),
2897  OUString(".uno:RunMacro"),
2898  OUString(".uno:SpacePara1"),
2899  OUString(".uno:SpacePara15"),
2900  OUString(".uno:SpacePara2")
2901  };
2902 
2903  util::URL aCommandURL;
2904  SfxViewShell* pViewShell = SfxViewShell::Current();
2905  SfxViewFrame* pViewFrame = pViewShell? pViewShell->GetViewFrame(): nullptr;
2906 
2907  // check if Frame-Controller were created.
2908  if (!pViewFrame)
2909  {
2910  SAL_WARN("lok", "iniUnoCommands: No Frame-Controller created.");
2911  return;
2912  }
2913 
2914  if (!xContext.is())
2916  if (!xContext.is())
2917  {
2918  SAL_WARN("lok", "iniUnoCommands: Component context is not available");
2919  return;
2920  }
2921 
2922  SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
2923  uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
2924 
2925  for (const auto & sUnoCommand : sUnoCommands)
2926  {
2927  aCommandURL.Complete = sUnoCommand;
2928  xParser->parseStrict(aCommandURL);
2929 
2930  // when null, this command is not supported by the given component
2931  // (like eg. Calc does not have ".uno:DefaultBullet" etc.)
2932  if (const SfxSlot* pSlot = rSlotPool.GetUnoSlot(aCommandURL.Path))
2933  {
2934  // Initialize slot to dispatch .uno: Command.
2935  pViewFrame->GetBindings().GetDispatch(pSlot, aCommandURL, false);
2936  }
2937  }
2938 }
2939 
2940 static int doc_getDocumentType (LibreOfficeKitDocument* pThis)
2941 {
2942  comphelper::ProfileZone aZone("doc_getDocumentType");
2943 
2944  SolarMutexGuard aGuard;
2946 
2947  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
2948 
2949  try
2950  {
2951  uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, uno::UNO_QUERY_THROW);
2952 
2953  if (xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
2954  {
2955  return LOK_DOCTYPE_SPREADSHEET;
2956  }
2957  else if (xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
2958  {
2959  return LOK_DOCTYPE_PRESENTATION;
2960  }
2961  else if (xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
2962  {
2963  return LOK_DOCTYPE_DRAWING;
2964  }
2965  else if (xDocument->supportsService("com.sun.star.text.TextDocument") || xDocument->supportsService("com.sun.star.text.WebDocument"))
2966  {
2967  return LOK_DOCTYPE_TEXT;
2968  }
2969  else
2970  {
2971  SetLastExceptionMsg("unknown document type");
2972  }
2973  }
2974  catch (const uno::Exception& exception)
2975  {
2976  SetLastExceptionMsg("exception: " + exception.Message);
2977  }
2978  return LOK_DOCTYPE_OTHER;
2979 }
2980 
2981 static int doc_getParts (LibreOfficeKitDocument* pThis)
2982 {
2983  comphelper::ProfileZone aZone("doc_getParts");
2984 
2985  SolarMutexGuard aGuard;
2986 
2987  ITiledRenderable* pDoc = getTiledRenderable(pThis);
2988  if (!pDoc)
2989  {
2990  SetLastExceptionMsg("Document doesn't support tiled rendering");
2991  return 0;
2992  }
2993 
2994  return pDoc->getParts();
2995 }
2996 
2997 static int doc_getPart (LibreOfficeKitDocument* pThis)
2998 {
2999  comphelper::ProfileZone aZone("doc_getPart");
3000 
3001  SolarMutexGuard aGuard;
3003 
3004  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3005  if (!pDoc)
3006  {
3007  SetLastExceptionMsg("Document doesn't support tiled rendering");
3008  return 0;
3009  }
3010 
3011  return pDoc->getPart();
3012 }
3013 
3014 static void doc_setPartImpl(LibreOfficeKitDocument* pThis, int nPart, bool bAllowChangeFocus = true)
3015 {
3016  comphelper::ProfileZone aZone("doc_setPart");
3017 
3018  SolarMutexGuard aGuard;
3020 
3021  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3022  if (!pDoc)
3023  {
3024  SetLastExceptionMsg("Document doesn't support tiled rendering");
3025  return;
3026  }
3027 
3028  pDoc->setPart( nPart, bAllowChangeFocus );
3029 }
3030 
3031 static void doc_setPart(LibreOfficeKitDocument* pThis, int nPart)
3032 {
3033  doc_setPartImpl(pThis, nPart, true);
3034 }
3035 
3036 static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart)
3037 {
3038  comphelper::ProfileZone aZone("doc_getPartInfo");
3039 
3040  SolarMutexGuard aGuard;
3041  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3042  if (!pDoc)
3043  {
3044  SetLastExceptionMsg("Document doesn't support tiled rendering");
3045  return nullptr;
3046  }
3047 
3048  return convertOUString(pDoc->getPartInfo(nPart));
3049 }
3050 
3051 static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect)
3052 {
3053  SolarMutexGuard aGuard;
3054  if (gImpl)
3055  gImpl->maLastExceptionMsg.clear();
3056 
3057  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3058  if (!pDoc)
3059  {
3060  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3061  return;
3062  }
3063 
3064  pDoc->selectPart( nPart, nSelect );
3065 }
3066 
3067 static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate)
3068 {
3069  SolarMutexGuard aGuard;
3070  if (gImpl)
3071  gImpl->maLastExceptionMsg.clear();
3072 
3073  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3074  if (!pDoc)
3075  {
3076  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3077  return;
3078  }
3079 
3080  pDoc->moveSelectedParts(nPosition, bDuplicate);
3081 }
3082 
3083 static char* doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
3084 {
3085  comphelper::ProfileZone aZone("doc_getPartPageRectangles");
3086 
3087  SolarMutexGuard aGuard;
3089 
3090  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3091  if (!pDoc)
3092  {
3093  SetLastExceptionMsg("Document doesn't support tiled rendering");
3094  return nullptr;
3095  }
3096 
3097  return convertOUString(pDoc->getPartPageRectangles());
3098 }
3099 
3100 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
3101 {
3102  comphelper::ProfileZone aZone("doc_getPartName");
3103 
3104  SolarMutexGuard aGuard;
3106 
3107  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3108  if (!pDoc)
3109  {
3110  SetLastExceptionMsg("Document doesn't support tiled rendering");
3111  return nullptr;
3112  }
3113 
3114  return convertOUString(pDoc->getPartName(nPart));
3115 }
3116 
3117 static char* doc_getPartHash(LibreOfficeKitDocument* pThis, int nPart)
3118 {
3119  comphelper::ProfileZone aZone("doc_getPartHash");
3120 
3121  SolarMutexGuard aGuard;
3123 
3124  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3125  if (!pDoc)
3126  {
3127  SetLastExceptionMsg("Document doesn't support tiled rendering");
3128  return nullptr;
3129  }
3130 
3131  return convertOUString(pDoc->getPartHash(nPart));
3132 }
3133 
3134 static void doc_setPartMode(LibreOfficeKitDocument* pThis,
3135  int nPartMode)
3136 {
3137  comphelper::ProfileZone aZone("doc_setPartMode");
3138 
3139  SolarMutexGuard aGuard;
3141 
3142  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3143  if (!pDoc)
3144  {
3145  SetLastExceptionMsg("Document doesn't support tiled rendering");
3146  return;
3147  }
3148 
3149 
3150  int nCurrentPart = pDoc->getPart();
3151 
3152  pDoc->setPartMode(nPartMode);
3153 
3154  // We need to make sure the internal state is updated, just changing the mode
3155  // might not update the relevant shells (i.e. impress will keep rendering the
3156  // previous mode unless we do this).
3157  // TODO: we might want to do this within the relevant components rather than
3158  // here, but that's also dependent on how we implement embedded object
3159  // rendering I guess?
3160  // TODO: we could be clever and e.g. set to 0 when we change to/from
3161  // embedded object mode, and not when changing between slide/notes/combined
3162  // modes?
3163  if ( nCurrentPart < pDoc->getParts() )
3164  {
3165  pDoc->setPart( nCurrentPart );
3166  }
3167  else
3168  {
3169  pDoc->setPart( 0 );
3170  }
3171 }
3172 
3173 static void doc_paintTile(LibreOfficeKitDocument* pThis,
3174  unsigned char* pBuffer,
3175  const int nCanvasWidth, const int nCanvasHeight,
3176  const int nTilePosX, const int nTilePosY,
3177  const int nTileWidth, const int nTileHeight)
3178 {
3179  comphelper::ProfileZone aZone("doc_paintTile");
3180 
3181  SolarMutexGuard aGuard;
3183 
3184  SAL_INFO( "lok.tiledrendering", "paintTile: painting [" << nTileWidth << "x" << nTileHeight <<
3185  "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3186  nCanvasWidth << "x" << nCanvasHeight << "]px" );
3187 
3188  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3189  if (!pDoc)
3190  {
3191  SetLastExceptionMsg("Document doesn't support tiled rendering");
3192  return;
3193  }
3194 
3195 #if defined(UNX) && !defined(MACOSX)
3196 
3197  // Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
3198  // and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
3199  // everything is painted bigger or smaller. This is different to what Calc's internal scaling
3200  // would do - because that one is trying to fit the lines between cells to integer multiples of
3201  // pixels.
3203 
3204 #if defined(IOS)
3205  double fDPIScaleX = 1.0;
3206  paintTileIOS(pThis, pBuffer, nCanvasWidth, nCanvasHeight, fDPIScaleX, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3207 #else
3208  ScopedVclPtrInstance< VirtualDevice > pDevice(DeviceFormat::DEFAULT);
3209 
3210  // Set background to transparent by default.
3211  pDevice->SetBackground(Wallpaper(COL_TRANSPARENT));
3212 
3213  pDevice->SetOutputSizePixelScaleOffsetAndBuffer(
3214  Size(nCanvasWidth, nCanvasHeight), Fraction(1.0), Point(),
3215  pBuffer);
3216 
3217  pDoc->paintTile(*pDevice, nCanvasWidth, nCanvasHeight,
3218  nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3219 
3220  static bool bDebug = getenv("LOK_DEBUG_TILES") != nullptr;
3221  if (bDebug)
3222  {
3223  // Draw a small red rectangle in the top left corner so that it's easy to see where a new tile begins.
3224  tools::Rectangle aRect(0, 0, 5, 5);
3225  aRect = pDevice->PixelToLogic(aRect);
3226  pDevice->Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
3227  pDevice->SetFillColor(COL_LIGHTRED);
3228  pDevice->SetLineColor();
3229  pDevice->DrawRect(aRect);
3230  pDevice->Pop();
3231  }
3232 #endif
3233 
3234 #else
3235  (void) pBuffer;
3236 #endif
3237 }
3238 
3239 #ifdef IOS
3240 
3241 // This function is separate only to be used by LibreOfficeLight. If that app can be retired, this
3242 // function's code can be inlined.
3243 static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
3244  void* rCGContext,
3245  const int nCanvasWidth, const int nCanvasHeight,
3246  const int nTilePosX, const int nTilePosY,
3247  const int nTileWidth, const int nTileHeight)
3248 {
3249  SolarMutexGuard aGuard;
3251 
3252  SAL_INFO( "lok.tiledrendering", "paintTileToCGContext: painting [" << nTileWidth << "x" << nTileHeight <<
3253  "]@(" << nTilePosX << ", " << nTilePosY << ") to [" <<
3254  nCanvasWidth << "x" << nCanvasHeight << "]px" );
3255 
3256  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3257  if (!pDoc)
3258  {
3259  SetLastExceptionMsg("Document doesn't support tiled rendering");
3260  return;
3261  }
3262 
3263  Size aCanvasSize(nCanvasWidth, nCanvasHeight);
3264  paintTileToCGContext(pDoc, rCGContext, aCanvasSize, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3265 }
3266 
3267 #endif
3268 
3269 static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
3270  unsigned char* pBuffer,
3271  const int nPart,
3272  const int nCanvasWidth, const int nCanvasHeight,
3273  const int nTilePosX, const int nTilePosY,
3274  const int nTileWidth, const int nTileHeight)
3275 {
3276  comphelper::ProfileZone aZone("doc_paintPartTile");
3277 
3278  SolarMutexGuard aGuard;
3280 
3281  SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " ["
3282  << nTileWidth << "x" << nTileHeight << "]@("
3283  << nTilePosX << ", " << nTilePosY << ") to ["
3284  << nCanvasWidth << "x" << nCanvasHeight << "]px" );
3285 
3286  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3287  int nOrigViewId = doc_getView(pThis);
3288 
3289  if (nOrigViewId < 0)
3290  {
3291  // tile painting always needs a SfxViewShell::Current(), but actually
3292  // it does not really matter which one - all of them should paint the
3293  // same thing. It's important to get a view for the correct document,
3294  // though.
3295  // doc_getViewsCount() returns the count of views for the document in the current view.
3296  int viewCount = doc_getViewsCount(pThis);
3297  if (viewCount == 0)
3298  return;
3299 
3300  std::vector<int> viewIds(viewCount);
3301  doc_getViewIds(pThis, viewIds.data(), viewCount);
3302 
3303  nOrigViewId = viewIds[0];
3304  doc_setView(pThis, nOrigViewId);
3305  }
3306 
3307  // Disable callbacks while we are painting.
3308  if (nOrigViewId >= 0)
3309  {
3310  const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
3311  if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
3312  handlerIt->second->disableCallbacks();
3313  }
3314 
3315  try
3316  {
3317  // Text documents have a single coordinate system; don't change part.
3318  int nOrigPart = 0;
3319  const bool isText = (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT);
3320  int nViewId = nOrigViewId;
3321  if (!isText)
3322  {
3323  // Check if just switching to another view is enough, that has
3324  // less side-effects.
3325  if (nPart != doc_getPart(pThis))
3326  {
3327  SfxViewShell* pViewShell = SfxViewShell::GetFirst();
3328  while (pViewShell)
3329  {
3330  if (pViewShell->getPart() == nPart)
3331  {
3332  nViewId = static_cast<sal_Int32>(pViewShell->GetViewShellId());
3333  doc_setView(pThis, nViewId);
3334  break;
3335  }
3336  pViewShell = SfxViewShell::GetNext(*pViewShell);
3337  }
3338  }
3339 
3340  nOrigPart = doc_getPart(pThis);
3341  if (nPart != nOrigPart)
3342  {
3343  doc_setPartImpl(pThis, nPart, false);
3344  }
3345  }
3346 
3347  doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
3348 
3349  if (!isText && nPart != nOrigPart)
3350  {
3351  doc_setPartImpl(pThis, nOrigPart, false);
3352  }
3353  if (!isText && nViewId != nOrigViewId)
3354  {
3355  doc_setView(pThis, nOrigViewId);
3356  }
3357  }
3358  catch (const std::exception&)
3359  {
3360  // Nothing to do but restore the PartTilePainting flag.
3361  }
3362 
3363  if (nOrigViewId >= 0)
3364  {
3365  const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
3366  if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
3367  handlerIt->second->enableCallbacks();
3368  }
3369 }
3370 
3371 static int doc_getTileMode(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
3372 {
3374  return LOK_TILEMODE_BGRA;
3375 }
3376 
3377 static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
3378  long* pWidth,
3379  long* pHeight)
3380 {
3381  comphelper::ProfileZone aZone("doc_getDocumentSize");
3382 
3383  SolarMutexGuard aGuard;
3385 
3386  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3387  if (pDoc)
3388  {
3389  Size aDocumentSize = pDoc->getDocumentSize();
3390  *pWidth = aDocumentSize.Width();
3391  *pHeight = aDocumentSize.Height();
3392  }
3393  else
3394  {
3395  SetLastExceptionMsg("Document doesn't support tiled rendering");
3396  }
3397 }
3398 
3399 static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
3400  const char* pArguments)
3401 {
3402  comphelper::ProfileZone aZone("doc_initializeForRendering");
3403 
3404  SolarMutexGuard aGuard;
3406 
3407  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3408  if (pDoc)
3409  {
3413  }
3414 }
3415 
3416 static void doc_registerCallback(LibreOfficeKitDocument* pThis,
3417  LibreOfficeKitCallback pCallback,
3418  void* pData)
3419 {
3420  SolarMutexGuard aGuard;
3422 
3423  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3424 
3425  const int nView = SfxLokHelper::getView();
3426  if (nView < 0)
3427  return;
3428 
3429  const size_t nId = nView;
3430  if (pCallback != nullptr)
3431  {
3432  for (auto& pair : pDocument->mpCallbackFlushHandlers)
3433  {
3434  if (pair.first == nId)
3435  continue;
3436 
3437  pair.second->addViewStates(nView);
3438  }
3439  }
3440  else
3441  {
3442  for (auto& pair : pDocument->mpCallbackFlushHandlers)
3443  {
3444  if (pair.first == nId)
3445  continue;
3446 
3447  pair.second->removeViewStates(nView);
3448  }
3449  }
3450 
3451  pDocument->mpCallbackFlushHandlers[nView] = std::make_shared<CallbackFlushHandler>(pThis, pCallback, pData);
3452 
3453  if (pCallback != nullptr)
3454  {
3455  for (const auto& pair : pDocument->mpCallbackFlushHandlers)
3456  {
3457  if (pair.first == nId)
3458  continue;
3459 
3460  pDocument->mpCallbackFlushHandlers[nView]->addViewStates(pair.first);
3461  }
3462 
3463  if (SfxViewShell* pViewShell = SfxViewShell::Current())
3464  {
3466  CallbackFlushHandler::callback, pDocument->mpCallbackFlushHandlers[nView].get());
3467  }
3468  }
3469  else
3470  {
3471  if (SfxViewShell* pViewShell = SfxViewShell::Current())
3472  {
3473  pViewShell->registerLibreOfficeKitViewCallback(nullptr, nullptr);
3474  }
3475  }
3476 }
3477 
3479 static char* getPostIts(LibreOfficeKitDocument* pThis)
3480 {
3482  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3483  if (!pDoc)
3484  {
3485  SetLastExceptionMsg("Document doesn't support tiled rendering");
3486  return nullptr;
3487  }
3488  tools::JsonWriter aJsonWriter;
3489  pDoc->getPostIts(aJsonWriter);
3490  return aJsonWriter.extractData();
3491 }
3492 
3494 static char* getPostItsPos(LibreOfficeKitDocument* pThis)
3495 {
3497  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3498  if (!pDoc)
3499  {
3500  SetLastExceptionMsg("Document doesn't support tiled rendering");
3501  return nullptr;
3502  }
3503  tools::JsonWriter aJsonWriter;
3504  pDoc->getPostItsPos(aJsonWriter);
3505  return aJsonWriter.extractData();
3506 }
3507 
3508 static char* getRulerState(LibreOfficeKitDocument* pThis)
3509 {
3511  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3512  if (!pDoc)
3513  {
3514  SetLastExceptionMsg("Document doesn't support tiled rendering");
3515  return nullptr;
3516  }
3517  tools::JsonWriter aJsonWriter;
3518  pDoc->getRulerState(aJsonWriter);
3519  return aJsonWriter.extractData();
3520 }
3521 
3522 static void doc_postKeyEvent(LibreOfficeKitDocument* pThis, int nType, int nCharCode, int nKeyCode)
3523 {
3524  comphelper::ProfileZone aZone("doc_postKeyEvent");
3525 
3526  SolarMutexGuard aGuard;
3528 
3529  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3530  if (!pDoc)
3531  {
3532  SetLastExceptionMsg("Document doesn't support tiled rendering");
3533  return;
3534  }
3535 
3536  try
3537  {
3538  pDoc->postKeyEvent(nType, nCharCode, nKeyCode);
3539  }
3540  catch (const uno::Exception& exception)
3541  {
3542  SetLastExceptionMsg(exception.Message);
3543  SAL_INFO("lok", "Failed to postKeyEvent " << exception.Message);
3544  }
3545 }
3546 
3547 static void doc_postWindowExtTextInputEvent(LibreOfficeKitDocument* pThis, unsigned nWindowId, int nType, const char* pText)
3548 {
3549  comphelper::ProfileZone aZone("doc_postWindowExtTextInputEvent");
3550 
3551  SolarMutexGuard aGuard;
3552  VclPtr<vcl::Window> pWindow;
3553  if (nWindowId == 0)
3554  {
3555  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3556  if (!pDoc)
3557  {
3558  SetLastExceptionMsg("Document doesn't support tiled rendering");
3559  return;
3560  }
3561  pWindow = pDoc->getDocWindow();
3562  }
3563  else
3564  {
3565  pWindow = vcl::Window::FindLOKWindow(nWindowId);
3566  }
3567 
3568  if (!pWindow)
3569  {
3570  SetLastExceptionMsg("No window found for window id: " + OUString::number(nWindowId));
3571  return;
3572  }
3573 
3574  SfxLokHelper::postExtTextEventAsync(pWindow, nType, OUString::fromUtf8(std::string_view(pText, strlen(pText))));
3575 }
3576 
3577 static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId, int nCharBefore, int nCharAfter)
3578 {
3579  SolarMutexGuard aGuard;
3580  VclPtr<vcl::Window> pWindow;
3581  if (nLOKWindowId == 0)
3582  {
3583  ITiledRenderable* pDoc = getTiledRenderable(pThis);
3584  if (!pDoc)
3585  {
3586  gImpl->maLastExceptionMsg = "Document doesn't support tiled rendering";
3587  return;
3588  }
3589  pWindow = pDoc->getDocWindow();
3590  }
3591  else
3592  {
3593  pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3594  }
3595 
3596  if (!pWindow)
3597  {
3598  gImpl->maLastExceptionMsg = "No window found for window id: " + OUString::number(nLOKWindowId);
3599  return;
3600  }
3601 
3602  // Annoyingly - backspace and delete are handled in the apps via an accelerator
3603  // which are PostMessage'd by SfxViewShell::ExecKey_Impl so to stay in the same
3604  // order we do this synchronously here, unless we're in a dialog.
3605  if (nCharBefore > 0)
3606  {
3607  // backspace
3608  if (nLOKWindowId == 0)
3609  {
3610  KeyEvent aEvt(8, 1283);
3611  for (int i = 0; i < nCharBefore; ++i)
3612  pWindow->KeyInput(aEvt);
3613  }
3614  else
3615  SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
3616  }
3617 
3618  if (nCharAfter > 0)
3619  {
3620  // delete (forward)
3621  if (nLOKWindowId == 0)
3622  {
3623  KeyEvent aEvt(46, 1286);
3624  for (int i = 0; i < nCharAfter; ++i)
3625  pWindow->KeyInput(aEvt);
3626  }
3627  else
3628  SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
3629  }
3630 }
3631 
3632 static void doc_postWindowKeyEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nCharCode, int nKeyCode)
3633 {
3634  comphelper::ProfileZone aZone("doc_postWindowKeyEvent");
3635 
3636  SolarMutexGuard aGuard;
3638 
3639  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
3640  if (!pWindow)
3641  {
3642  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3643  return;
3644  }
3645 
3646  KeyEvent aEvent(nCharCode, nKeyCode, 0);
3647 
3648  switch (nType)
3649  {
3650  case LOK_KEYEVENT_KEYINPUT:
3651  Application::PostKeyEvent(VclEventId::WindowKeyInput, pWindow, &aEvent);
3652  break;
3653  case LOK_KEYEVENT_KEYUP:
3654  Application::PostKeyEvent(VclEventId::WindowKeyUp, pWindow, &aEvent);
3655  break;
3656  default:
3657  assert(false);
3658  break;
3659  }
3660 }
3661 
3662 static size_t doc_renderShapeSelection(LibreOfficeKitDocument* pThis, char** pOutput)
3663 {
3664  comphelper::ProfileZone aZone("doc_renderShapeSelection");
3665 
3666  SolarMutexGuard aGuard;
3668 
3669  LokChartHelper aChartHelper(SfxViewShell::Current());
3670 
3671  if (aChartHelper.GetWindow())
3672  return 0;
3673 
3674  try
3675  {
3676  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3677 
3678  uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3679 
3680  SvMemoryStream aOutStream;
3681  uno::Reference<io::XOutputStream> xOut = new utl::OOutputStreamWrapper(aOutStream);
3682 
3683  utl::MediaDescriptor aMediaDescriptor;
3684  switch (doc_getDocumentType(pThis))
3685  {
3686  case LOK_DOCTYPE_PRESENTATION:
3687  aMediaDescriptor["FilterName"] <<= OUString("impress_svg_Export");
3688  break;
3689  case LOK_DOCTYPE_DRAWING:
3690  aMediaDescriptor["FilterName"] <<= OUString("draw_svg_Export");
3691  break;
3692  case LOK_DOCTYPE_TEXT:
3693  aMediaDescriptor["FilterName"] <<= OUString("writer_svg_Export");
3694  break;
3695  case LOK_DOCTYPE_SPREADSHEET:
3696  aMediaDescriptor["FilterName"] <<= OUString("calc_svg_Export");
3697  break;
3698  default:
3699  SAL_WARN("lok", "Failed to render shape selection: Document type is not supported");
3700  }
3701  aMediaDescriptor["SelectionOnly"] <<= true;
3702  aMediaDescriptor["OutputStream"] <<= xOut;
3703 
3704  xStorable->storeToURL("private:stream", aMediaDescriptor.getAsConstPropertyValueList());
3705 
3706  if (pOutput)
3707  {
3708  const size_t nOutputSize = aOutStream.GetEndOfData();
3709  *pOutput = static_cast<char*>(malloc(nOutputSize));
3710  if (*pOutput)
3711  {
3712  std::memcpy(*pOutput, aOutStream.GetData(), nOutputSize);
3713  return nOutputSize;
3714  }
3715  }
3716  }
3717  catch (const uno::Exception& exception)
3718  {
3719  css::uno::Any exAny( cppu::getCaughtException() );
3720  SetLastExceptionMsg(exception.Message);
3721  SAL_WARN("lok", "Failed to render shape selection: " << exceptionToString(exAny));
3722  }
3723 
3724  return 0;
3725 }
3726 
3727 namespace {
3728 
3736 class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
3737 {
3738  OString maCommand;
3739  std::shared_ptr<CallbackFlushHandler> mpCallback;
3740 
3741 public:
3742  DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> const & pCallback)
3743  : maCommand(pCommand)
3744  , mpCallback(pCallback)
3745  {
3746  assert(mpCallback);
3747  }
3748 
3749  virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override
3750  {
3751  boost::property_tree::ptree aTree;
3752  aTree.put("commandName", maCommand.getStr());
3753 
3754  if (rEvent.State != frame::DispatchResultState::DONTKNOW)
3755  {
3756  bool bSuccess = (rEvent.State == frame::DispatchResultState::SUCCESS);
3757  aTree.put("success", bSuccess);
3758  }
3759 
3760  aTree.add_child("result", unoAnyToPropertyTree(rEvent.Result));
3761 
3762  std::stringstream aStream;
3763  boost::property_tree::write_json(aStream, aTree);
3764  OString aPayload = aStream.str().c_str();
3765  mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3766  }
3767 
3768  virtual void SAL_CALL disposing(const css::lang::EventObject&) override {}
3769 };
3770 
3771 } // anonymous namespace
3772 
3773 
3774 static void lcl_sendDialogEvent(unsigned long long int nWindowId, const char* pArguments)
3775 {
3776  SolarMutexGuard aGuard;
3777 
3779  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nWindowId);
3780 
3781  if (!pWindow && nWindowId >= 1000000000 /* why unsigned? */)
3782  pWindow = getSidebarWindow();
3783 
3784  if (aMap.find("id") == aMap.end())
3785  return;
3786 
3787  static constexpr OUStringLiteral sClickAction(u"CLICK");
3788  static constexpr OUStringLiteral sSelectAction(u"SELECT");
3789  static constexpr OUStringLiteral sClearAction(u"CLEAR");
3790  static constexpr OUStringLiteral sTypeAction(u"TYPE");
3791  static constexpr OUStringLiteral sUpAction(u"UP");
3792  static constexpr OUStringLiteral sDownAction(u"DOWN");
3793  static constexpr OUStringLiteral sValue(u"VALUE");
3794 
3795  bool bIsWeldedDialog = false;
3796 
3797  try
3798  {
3799  OString sControlId = OUStringToOString(aMap["id"], RTL_TEXTENCODING_ASCII_US);
3800 
3801  bIsWeldedDialog = jsdialog::ExecuteAction(nWindowId, sControlId, aMap);
3802  if (!bIsWeldedDialog)
3803  bIsWeldedDialog = jsdialog::ExecuteAction(reinterpret_cast<sal_uInt64>(SfxViewShell::Current()),
3804  sControlId, aMap);
3805 
3806  if (!pWindow)
3807  {
3808  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
3809  return;
3810  }
3811 
3812  if (!bIsWeldedDialog)
3813  {
3814  WindowUIObject aUIObject(pWindow);
3815  std::unique_ptr<UIObject> pUIWindow(aUIObject.get_visible_child(aMap["id"]));
3816  if (pUIWindow) {
3817  OUString sAction((aMap.find("cmd") != aMap.end())? aMap["cmd"]: "");
3818 
3819  if (sAction == "selected")
3820  {
3821  aMap["POS"] = aMap["data"];
3822  aMap["TEXT"] = aMap["data"];
3823 
3824  pUIWindow->execute(sSelectAction, aMap);
3825  }
3826  else if (sAction == "plus")
3827  {
3828  pUIWindow->execute(sUpAction, aMap);
3829  }
3830  else if (sAction == "minus")
3831  {
3832  pUIWindow->execute(sDownAction, aMap);
3833  }
3834  else if (sAction == "set")
3835  {
3836  aMap["TEXT"] = aMap["data"];
3837 
3838  pUIWindow->execute(sClearAction, aMap);
3839  pUIWindow->execute(sTypeAction, aMap);
3840  }
3841  else if (sAction == "value")
3842  {
3843  aMap["VALUE"] = aMap["data"];
3844  pUIWindow->execute(sValue, aMap);
3845  }
3846  else if (sAction == "click" && aMap["type"] == "drawingarea")
3847  {
3848  int separatorPos = aMap["data"].indexOf(';');
3849  if (separatorPos > 0)
3850  {
3851  // x;y
3852  aMap["POSX"] = aMap["data"].copy(0, separatorPos);
3853  aMap["POSY"] = aMap["data"].copy(separatorPos + 1);
3854  }
3855  pUIWindow->execute(sClickAction, aMap);
3856  }
3857  else
3858  pUIWindow->execute(sClickAction, aMap);
3859  }
3860  }
3861  } catch(...) {}
3862 
3863  // force resend
3864  if (!bIsWeldedDialog)
3865  pWindow->Resize();
3866 }
3867 
3868 
3869 static void doc_sendDialogEvent(LibreOfficeKitDocument* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
3870 {
3871  lcl_sendDialogEvent(nWindowId, pArguments);
3872 }
3873 
3874 static void lo_sendDialogEvent(LibreOfficeKit* /*pThis*/, unsigned long long int nWindowId, const char* pArguments)
3875 {
3876  lcl_sendDialogEvent(nWindowId, pArguments);
3877 }
3878 
3879 static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const char* pValue)
3880 {
3881  static char* pCurrentSalLogOverride = nullptr;
3882 
3883  if (strcmp(pOption, "traceeventrecording") == 0)
3884  {
3885  if (strcmp(pValue, "start") == 0)
3886  {
3888  if (traceEventDumper == nullptr)
3889  traceEventDumper = new TraceEventDumper();
3890  }
3891  else if (strcmp(pValue, "stop") == 0)
3893  }
3894  else if (strcmp(pOption, "sallogoverride") == 0)
3895  {
3896  if (pCurrentSalLogOverride != nullptr)
3897  free(pCurrentSalLogOverride);
3898  if (pValue == nullptr)
3899  pCurrentSalLogOverride = nullptr;
3900  else
3901  pCurrentSalLogOverride = strdup(pValue);
3902 
3903  if (pCurrentSalLogOverride == nullptr || pCurrentSalLogOverride[0] == '\0')
3904  sal_detail_set_log_selector(nullptr);
3905  else
3906  sal_detail_set_log_selector(pCurrentSalLogOverride);
3907  }
3908 }
3909 
3910 static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
3911 {
3912  comphelper::ProfileZone aZone("doc_postUnoCommand");
3913 
3914  SolarMutexGuard aGuard;
3916 
3918  OUString aCommand(pCommand, strlen(pCommand), RTL_TEXTENCODING_UTF8);
3919  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
3920 
3921  std::vector<beans::PropertyValue> aPropertyValuesVector(jsonToPropertyValuesVector(pArguments));
3922 
3923  if (!vcl::lok::isUnipoll())
3924  {
3925  beans::PropertyValue aSynchronMode;
3926  aSynchronMode.Name = "SynchronMode";
3927  aSynchronMode.Value <<= false;
3928  aPropertyValuesVector.push_back(aSynchronMode);
3929  }
3930 
3931  int nView = SfxLokHelper::getView();
3932  if (nView < 0)
3933  return;
3934 
3935  if (gImpl && aCommand == ".uno:ToggleOrientation")
3936  {
3937  ExecuteOrientationChange();
3938  return;
3939  }
3940 
3941  // handle potential interaction
3942  if (gImpl && aCommand == ".uno:Save")
3943  {
3944  // Check if saving a PDF file
3945  OUString aMimeType = lcl_getCurrentDocumentMimeType(pDocument);
3946  if (pDocSh->IsModified() && aMimeType == "application/pdf")
3947  {
3948  // If we have a PDF file (for saving annotations for example), we need
3949  // to run save-as to the same file as the opened document. Plain save
3950  // doesn't work as the PDF is not a "native" format.
3951  uno::Reference<frame::XStorable> xStorable(pDocument->mxComponent, uno::UNO_QUERY_THROW);
3952  OUString aURL = xStorable->getLocation();
3953  OString aURLUtf8 = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
3954  bool bResult = doc_saveAs(pThis, aURLUtf8.getStr(), "pdf", nullptr);
3955 
3956  // Send the result of save
3957  boost::property_tree::ptree aTree;
3958  aTree.put("commandName", pCommand);
3959  aTree.put("success", bResult);
3960  std::stringstream aStream;
3961  boost::property_tree::write_json(aStream, aTree);
3962  OString aPayload = aStream.str().c_str();
3963  pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
3964  return;
3965  }
3966 
3967 
3968  rtl::Reference<LOKInteractionHandler> const pInteraction(
3969  new LOKInteractionHandler("save", gImpl, pDocument));
3970  uno::Reference<task::XInteractionHandler2> const xInteraction(pInteraction);
3971 
3972  beans::PropertyValue aValue;
3973  aValue.Name = "InteractionHandler";
3974  aValue.Value <<= xInteraction;
3975  aPropertyValuesVector.push_back(aValue);
3976 
3977  bool bDontSaveIfUnmodified = false;
3978  aPropertyValuesVector.erase(std::remove_if(aPropertyValuesVector.begin(),
3979  aPropertyValuesVector.end(),
3980  [&bDontSaveIfUnmodified](const beans::PropertyValue& aItem){
3981  if (aItem.Name == "DontSaveIfUnmodified")
3982  {
3983  bDontSaveIfUnmodified = aItem.Value.get<bool>();
3984  return true;
3985  }
3986  return false;
3987  }), aPropertyValuesVector.end());
3988 
3989  // skip saving and tell the result via UNO_COMMAND_RESULT
3990  if (bDontSaveIfUnmodified && !pDocSh->IsModified())
3991  {
3992  boost::property_tree::ptree aTree;
3993  aTree.put("commandName", pCommand);
3994  aTree.put("success", false);
3995 
3996  // Add the reason for not saving
3997  const uno::Any aResultValue = uno::makeAny(OUString("unmodified"));
3998  aTree.add_child("result", unoAnyToPropertyTree(aResultValue));
3999 
4000  std::stringstream aStream;
4001  boost::property_tree::write_json(aStream, aTree);
4002  OString aPayload = aStream.str().c_str();
4003  pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aPayload.getStr());
4004  return;
4005  }
4006  }
4007  else if (gImpl && aCommand == ".uno:TransformDialog")
4008  {
4009  bool bNeedConversion = false;
4010  SfxViewShell* pViewShell = SfxViewShell::Current();
4011  LokChartHelper aChartHelper(pViewShell);
4012 
4013  if (aChartHelper.GetWindow() )
4014  {
4015  bNeedConversion = true;
4016  }
4017  else if (const SdrView* pView = pViewShell->GetDrawView())
4018  {
4019  if (OutputDevice* pOutputDevice = pView->GetFirstOutputDevice())
4020  {
4021  bNeedConversion = (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM);
4022  }
4023  }
4024 
4025  if (bNeedConversion)
4026  {
4027  sal_Int32 value;
4028  for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4029  {
4030  if (rPropValue.Name == "TransformPosX"
4031  || rPropValue.Name == "TransformPosY"
4032  || rPropValue.Name == "TransformWidth"
4033  || rPropValue.Name == "TransformHeight"
4034  || rPropValue.Name == "TransformRotationX"
4035  || rPropValue.Name == "TransformRotationY")
4036  {
4037  rPropValue.Value >>= value;
4038  value = OutputDevice::LogicToLogic(value, MapUnit::MapTwip, MapUnit::Map100thMM);
4039  rPropValue.Value <<= value;
4040  }
4041  }
4042  }
4043 
4044  if (aChartHelper.GetWindow() && aPropertyValuesVector.size() > 0)
4045  {
4046  if (aPropertyValuesVector[0].Name != "Action")
4047  {
4048  tools::Rectangle aChartBB = aChartHelper.GetChartBoundingBox();
4049  int nLeft = OutputDevice::LogicToLogic(aChartBB.Left(), MapUnit::MapTwip, MapUnit::Map100thMM);
4050  int nTop = OutputDevice::LogicToLogic(aChartBB.Top(), MapUnit::MapTwip, MapUnit::Map100thMM);
4051 
4052  for (beans::PropertyValue& rPropValue: aPropertyValuesVector)
4053  {
4054  if (rPropValue.Name == "TransformPosX" || rPropValue.Name == "TransformRotationX")
4055  {
4056  auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4057  rPropValue.Value <<= value - nLeft;
4058  }
4059  else if (rPropValue.Name == "TransformPosY" || rPropValue.Name == "TransformRotationY")
4060  {
4061  auto const value = *o3tl::doAccess<sal_Int32>(rPropValue.Value);
4062  rPropValue.Value <<= value - nTop;
4063  }
4064  }
4065  }
4066  util::URL aCommandURL;
4067  aCommandURL.Path = "LOKTransform";
4068  css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
4069  aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
4070  return;
4071  }
4072  }
4073  else if (gImpl && aCommand == ".uno:LOKSidebarWriterPage")
4074  {
4075  setupSidebar(u"WriterPageDeck");
4076  return;
4077  }
4078  else if (gImpl && aCommand == ".uno:SidebarShow")
4079  {
4080  setupSidebar();
4081  return;
4082  }
4083  else if (gImpl && aCommand == ".uno:SidebarHide")
4084  {
4085  hideSidebar();
4086  return;
4087  }
4088 
4089  bool bResult = false;
4090  LokChartHelper aChartHelper(SfxViewShell::Current());
4091 
4092  if (aChartHelper.GetWindow() && aCommand != ".uno:Save" )
4093  {
4094  util::URL aCommandURL;
4095  aCommandURL.Path = aCommand.copy(5);
4096  css::uno::Reference<css::frame::XDispatch>& aChartDispatcher = aChartHelper.GetXDispatcher();
4097  aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
4098  return;
4099  }
4100  else if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
4101  {
4102  bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
4103  new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
4104  }
4105  else
4106  bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
4107 
4108  if (!bResult)
4109  {
4110  SetLastExceptionMsg("Failed to dispatch " + aCommand);
4111  }
4112 }
4113 
4114 static void doc_postMouseEvent(LibreOfficeKitDocument* pThis, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
4115 {
4116  comphelper::ProfileZone aZone("doc_postMouseEvent");
4117 
4118  SolarMutexGuard aGuard;
4120 
4121  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4122  if (!pDoc)
4123  {
4124  SetLastExceptionMsg("Document doesn't support tiled rendering");
4125  return;
4126  }
4127  try
4128  {
4129  pDoc->postMouseEvent(nType, nX, nY, nCount, nButtons, nModifier);
4130  }
4131  catch (const uno::Exception& exception)
4132  {
4133  SetLastExceptionMsg(exception.Message);
4134  SAL_INFO("lok", "Failed to postMouseEvent " << exception.Message);
4135  }
4136 }
4137 
4138 static void doc_postWindowMouseEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, int nType, int nX, int nY, int nCount, int nButtons, int nModifier)
4139 {
4140  comphelper::ProfileZone aZone("doc_postWindowMouseEvent");
4141 
4142  SolarMutexGuard aGuard;
4144 
4145  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4146  if (!pWindow)
4147  {
4148  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4149  return;
4150  }
4151 
4152  const Point aPos(nX, nY);
4153 
4154  MouseEvent aEvent(aPos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
4155 
4156  vcl::EnableDialogInput(pWindow);
4157 
4158  switch (nType)
4159  {
4160  case LOK_MOUSEEVENT_MOUSEBUTTONDOWN:
4161  Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aEvent);
4162  break;
4163  case LOK_MOUSEEVENT_MOUSEBUTTONUP:
4164  Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aEvent);
4165  break;
4166  case LOK_MOUSEEVENT_MOUSEMOVE:
4167  Application::PostMouseEvent(VclEventId::WindowMouseMove, pWindow, &aEvent);
4168  break;
4169  default:
4170  assert(false);
4171  break;
4172  }
4173 }
4174 
4175 static void doc_postWindowGestureEvent(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, const char* pType, int nX, int nY, int nOffset)
4176 {
4177  comphelper::ProfileZone aZone("doc_postWindowGestureEvent");
4178 
4179  SolarMutexGuard aGuard;
4181 
4182  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4183  if (!pWindow)
4184  {
4185  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4186  return;
4187  }
4188 
4189  OString aType(pType);
4190  GestureEventType eEventType = GestureEventType::PanningUpdate;
4191 
4192  if (aType == "panBegin")
4193  eEventType = GestureEventType::PanningBegin;
4194  else if (aType == "panEnd")
4195  eEventType = GestureEventType::PanningEnd;
4196 
4198  sal_Int32(nX),
4199  sal_Int32(nY),
4200  eEventType,
4201  sal_Int32(nOffset),
4202  PanningOrientation::Vertical,
4203  };
4204 
4205  vcl::EnableDialogInput(pWindow);
4206 
4207  Application::PostGestureEvent(VclEventId::WindowGestureEvent, pWindow, &aEvent);
4208 }
4209 
4210 static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
4211 {
4212  comphelper::ProfileZone aZone("doc_setTextSelection");
4213 
4214  SolarMutexGuard aGuard;
4216 
4217  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4218  if (!pDoc)
4219  {
4220  SetLastExceptionMsg("Document doesn't support tiled rendering");
4221  return;
4222  }
4223 
4224  pDoc->setTextSelection(nType, nX, nY);
4225 }
4226 
4227 static void doc_setWindowTextSelection(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindowId, bool swap, int nX, int nY)
4228 {
4229  comphelper::ProfileZone aZone("doc_setWindowTextSelection");
4230 
4231  SolarMutexGuard aGuard;
4233 
4234  VclPtr<Window> pWindow = vcl::Window::FindLOKWindow(nLOKWindowId);
4235  if (!pWindow)
4236  {
4237  SetLastExceptionMsg("Document doesn't support dialog rendering, or window not found.");
4238  return;
4239  }
4240 
4241 
4242  Size aOffset(pWindow->GetOutOffXPixel(), pWindow->GetOutOffYPixel());
4243  Point aCursorPos(nX, nY);
4244  aCursorPos.Move(aOffset);
4245  sal_uInt16 nModifier = swap ? KEY_MOD1 + KEY_MOD2 : KEY_SHIFT;
4246 
4247  MouseEvent aCursorEvent(aCursorPos, 1, MouseEventModifiers::SIMPLECLICK, 0, nModifier);
4248  Application::PostMouseEvent(VclEventId::WindowMouseButtonDown, pWindow, &aCursorEvent);
4249  Application::PostMouseEvent(VclEventId::WindowMouseButtonUp, pWindow, &aCursorEvent);
4250 }
4251 
4252 static bool getFromTransferrable(
4253  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
4254  const OString &aInMimeType, OString &aRet);
4255 
4256 static bool encodeImageAsHTML(
4257  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
4258  const OString &aMimeType, OString &aRet)
4259 {
4260  if (!getFromTransferrable(xTransferable, aMimeType, aRet))
4261  return false;
4262 
4263  // Encode in base64.
4264  auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
4265  aRet.getLength());
4266  OUStringBuffer aBase64Data;
4267  comphelper::Base64::encode(aBase64Data, aSeq);
4268 
4269  // Embed in HTML.
4270  aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
4271  "<html><head>"
4272  "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
4273  "name=\"generator\" content=\""
4274  + getGenerator().toUtf8()
4275  + "\"/>"
4276  "</head><body><img src=\"data:" + aMimeType + ";base64,"
4277  + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>";
4278 
4279  return true;
4280 }
4281 
4282 static bool encodeTextAsHTML(
4283  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
4284  const OString &aMimeType, OString &aRet)
4285 {
4286  if (!getFromTransferrable(xTransferable, aMimeType, aRet))
4287  return false;
4288 
4289  // Embed in HTML - FIXME: needs some escaping.
4290  aRet = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
4291  "<html><head>"
4292  "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/><meta "
4293  "name=\"generator\" content=\""
4294  + getGenerator().toUtf8()
4295  + "\"/></head><body><pre>" + aRet + "</pre></body></html>";
4296 
4297  return true;
4298 }
4299 
4301  const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
4302  const OString &aInMimeType, OString &aRet)
4303 {
4304  OString aMimeType(aInMimeType);
4305 
4306  // Take care of UTF-8 text here.
4307  bool bConvert = false;
4308  sal_Int32 nIndex = 0;
4309  if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
4310  {
4311  if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
4312  {
4313  aMimeType = "text/plain;charset=utf-16";
4314  bConvert = true;
4315  }
4316  }
4317 
4318  datatransfer::DataFlavor aFlavor;
4319  aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
4320  if (aMimeType == "text/plain;charset=utf-16")
4321  aFlavor.DataType = cppu::UnoType<OUString>::get();
4322  else
4323  aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();
4324 
4325  if (!xTransferable->isDataFlavorSupported(aFlavor))
4326  {
4327  // Try harder for HTML it is our copy/paste meta-file format
4328  if (aInMimeType == "text/html")
4329  {
4330  // Desperate measures - convert text to HTML instead.
4331  if (encodeTextAsHTML(xTransferable, "text/plain;charset=utf-8", aRet))
4332  return true;
4333  // If html is not supported, might be a graphic-selection,
4334  if (encodeImageAsHTML(xTransferable, "image/png", aRet))
4335  return true;
4336  }
4337 
4338  SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
4339  return false;
4340  }
4341 
4342  uno::Any aAny;
4343  try
4344  {
4345  aAny = xTransferable->getTransferData(aFlavor);
4346  }
4347  catch (const css::datatransfer::UnsupportedFlavorException& e)
4348  {
4349  SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
4350  return false;
4351  }
4352  catch (const css::uno::Exception& e)
4353  {
4354  SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
4355  return false;
4356  }
4357 
4358  if (aFlavor.DataType == cppu::UnoType<OUString>::get())
4359  {
4360  OUString aString;
4361  aAny >>= aString;
4362  if (bConvert)
4363  aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
4364  else
4365  aRet = OString(reinterpret_cast<const char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
4366  }
4367  else
4368  {
4369  uno::Sequence<sal_Int8> aSequence;
4370  aAny >>= aSequence;
4371  aRet = OString(reinterpret_cast<char*>(aSequence.getArray()), aSequence.getLength());
4372  }
4373 
4374  return true;
4375 }
4376 
4377 static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
4378 {
4379  comphelper::ProfileZone aZone("doc_getTextSelection");
4380 
4381  SolarMutexGuard aGuard;
4383 
4384  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4385  if (!pDoc)
4386  {
4387  SetLastExceptionMsg("Document doesn't support tiled rendering");
4388  return nullptr;
4389  }
4390 
4391  css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
4392  if (!xTransferable)
4393  {
4394  SetLastExceptionMsg("No selection available");
4395  return nullptr;
4396  }
4397 
4398  const char *pType = pMimeType;
4399  if (!pType || pType[0] == '\0')
4400  pType = "text/plain;charset=utf-8";
4401 
4402  OString aRet;
4403  bool bSuccess = getFromTransferrable(xTransferable, OString(pType), aRet);
4404  if (!bSuccess)
4405  return nullptr;
4406 
4407  if (pUsedMimeType) // legacy
4408  {
4409  if (pMimeType)
4410  *pUsedMimeType = strdup(pMimeType);
4411  else
4412  *pUsedMimeType = nullptr;
4413  }
4414 
4415  return convertOString(aRet);
4416 }
4417 
4418 static int doc_getSelectionType(LibreOfficeKitDocument* pThis)
4419 {
4420  comphelper::ProfileZone aZone("doc_getSelectionType");
4421 
4422  SolarMutexGuard aGuard;
4424 
4425  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4426  if (!pDoc)
4427  {
4428  SetLastExceptionMsg("Document doesn't support tiled rendering");
4429  return LOK_SELTYPE_NONE;
4430  }
4431 
4432  css::uno::Reference<css::datatransfer::XTransferable2> xTransferable(pDoc->getSelection(), css::uno::UNO_QUERY);
4433  if (!xTransferable)
4434  {
4435  SetLastExceptionMsg("No selection available");
4436  return LOK_SELTYPE_NONE;
4437  }
4438 
4439  if (xTransferable->isComplex())
4440  return LOK_SELTYPE_COMPLEX;
4441 
4442  OString aRet;
4443  bool bSuccess = getFromTransferrable(xTransferable, "text/plain;charset=utf-8", aRet);
4444  if (!bSuccess)
4445  return LOK_SELTYPE_NONE;
4446 
4447  if (aRet.getLength() > 10000)
4448  return LOK_SELTYPE_COMPLEX;
4449 
4450  return aRet.getLength() ? LOK_SELTYPE_TEXT : LOK_SELTYPE_NONE;
4451 }
4452 
4453 static int doc_getClipboard(LibreOfficeKitDocument* pThis,
4454  const char **pMimeTypes,
4455  size_t *pOutCount,
4456  char ***pOutMimeTypes,
4457  size_t **pOutSizes,
4458  char ***pOutStreams)
4459 {
4460  comphelper::ProfileZone aZone("doc_getClipboard");
4461 
4462  SolarMutexGuard aGuard;
4464 
4465  assert (pOutCount);
4466  assert (pOutMimeTypes);
4467  assert (pOutSizes);
4468  assert (pOutStreams);
4469 
4470  *pOutCount = 0;
4471  *pOutMimeTypes = nullptr;
4472  *pOutSizes = nullptr;
4473  *pOutStreams = nullptr;
4474 
4475  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4476  if (!pDoc)
4477  {
4478  SetLastExceptionMsg("Document doesn't support tiled rendering");
4479  return 0;
4480  }
4481 
4483 
4484  css::uno::Reference<css::datatransfer::XTransferable> xTransferable = xClip->getContents();
4485  SAL_INFO("lok", "Got from clip: " << xClip.get() << " transferrable: " << xTransferable);
4486  if (!xTransferable)
4487  {
4488  SetLastExceptionMsg("No clipboard content available");
4489  return 0;
4490  }
4491 
4492  std::vector<OString> aMimeTypes;
4493  if (!pMimeTypes) // everything
4494  {
4495  const uno::Sequence< css::datatransfer::DataFlavor > flavors = xTransferable->getTransferDataFlavors();
4496  if (!flavors.getLength())
4497  {
4498  SetLastExceptionMsg("Flavourless selection");
4499  return 0;
4500  }
4501  for (const auto &it : flavors)
4502  aMimeTypes.push_back(OUStringToOString(it.MimeType, RTL_TEXTENCODING_UTF8));
4503  }
4504  else
4505  {
4506  for (size_t i = 0; pMimeTypes[i]; ++i)
4507  aMimeTypes.push_back(OString(pMimeTypes[i]));
4508  }
4509 
4510  *pOutCount = aMimeTypes.size();
4511  *pOutSizes = static_cast<size_t *>(malloc(*pOutCount * sizeof(size_t)));
4512  *pOutMimeTypes = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
4513  *pOutStreams = static_cast<char **>(malloc(*pOutCount * sizeof(char *)));
4514  for (size_t i = 0; i < aMimeTypes.size(); ++i)
4515  {
4516  if (aMimeTypes[i] == "text/plain;charset=utf-16")
4517  (*pOutMimeTypes)[i] = strdup("text/plain;charset=utf-8");
4518  else
4519  (*pOutMimeTypes)[i] = strdup(aMimeTypes[i].getStr());
4520 
4521  OString aRet;
4522  bool bSuccess = getFromTransferrable(xTransferable, (*pOutMimeTypes)[i], aRet);
4523  if (!bSuccess || aRet.getLength() < 1)
4524  {
4525  (*pOutSizes)[i] = 0;
4526  (*pOutStreams)[i] = nullptr;
4527  }
4528  else
4529  {
4530  (*pOutSizes)[i] = aRet.getLength();
4531  (*pOutStreams)[i] = convertOString(aRet);
4532  }
4533  }
4534 
4535  return 1;
4536 }
4537 
4538 static int doc_setClipboard(LibreOfficeKitDocument* pThis,
4539  const size_t nInCount,
4540  const char **pInMimeTypes,
4541  const size_t *pInSizes,
4542  const char **pInStreams)
4543 {
4544 #ifdef IOS
4545  (void) pThis;
4546  (void) nInCount;
4547  (void) pInMimeTypes;
4548  (void) pInSizes;
4549  (void) pInStreams;
4550 #else
4551  comphelper::ProfileZone aZone("doc_setClipboard");
4552 
4553  SolarMutexGuard aGuard;
4555 
4556  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4557  if (!pDoc)
4558  {
4559  SetLastExceptionMsg("Document doesn't support tiled rendering");
4560  return false;
4561  }
4562 
4563  uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable(nInCount, pInMimeTypes, pInSizes, pInStreams));
4564 
4565  auto xClip = forceSetClipboardForCurrentView(pThis);
4566  xClip->setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
4567 
4568  SAL_INFO("lok", "Set clip: " << xClip.get() << " to: " << xTransferable);
4569 
4570  if (!pDoc->isMimeTypeSupported())
4571  {
4572  SetLastExceptionMsg("Document doesn't support this mime type");
4573  return false;
4574  }
4575 #endif
4576  return true;
4577 }
4578 
4579 static bool doc_paste(LibreOfficeKitDocument* pThis, const char* pMimeType, const char* pData, size_t nSize)
4580 {
4581  comphelper::ProfileZone aZone("doc_paste");
4582 
4583  SolarMutexGuard aGuard;
4584 
4585  const char *pInMimeTypes[1];
4586  const char *pInStreams[1];
4587  size_t pInSizes[1];
4588  pInMimeTypes[0] = pMimeType;
4589  pInSizes[0] = nSize;
4590  pInStreams[0] = pData;
4591 
4592  if (!doc_setClipboard(pThis, 1, pInMimeTypes, pInSizes, pInStreams))
4593  return false;
4594 
4595  uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
4596  {
4597  {"AnchorType", uno::makeAny(static_cast<sal_uInt16>(text::TextContentAnchorType_AS_CHARACTER))},
4598  {"IgnoreComments", uno::makeAny(true)},
4599  }));
4600  if (!comphelper::dispatchCommand(".uno:Paste", aPropertyValues))
4601  {
4602  SetLastExceptionMsg("Failed to dispatch the .uno: command");
4603  return false;
4604  }
4605 
4606  return true;
4607 }
4608 
4609 static void doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY)
4610 {
4611  comphelper::ProfileZone aZone("doc_setGraphicSelection");
4612 
4613  SolarMutexGuard aGuard;
4615 
4616  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4617  if (!pDoc)
4618  {
4619  SetLastExceptionMsg("Document doesn't support tiled rendering");
4620  return;
4621  }
4622 
4623  pDoc->setGraphicSelection(nType, nX, nY);
4624 }
4625 
4626 static void doc_resetSelection(LibreOfficeKitDocument* pThis)
4627 {
4628  comphelper::ProfileZone aZone("doc_resetSelection");
4629 
4630  SolarMutexGuard aGuard;
4632 
4633  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4634  if (!pDoc)
4635  {
4636  SetLastExceptionMsg("Document doesn't support tiled rendering");
4637  return;
4638  }
4639 
4640  pDoc->resetSelection();
4641 }
4642 
4643 static char* getLanguages(const char* pCommand)
4644 {
4645  css::uno::Sequence< css::lang::Locale > aLocales;
4646 
4647  if (xContext.is())
4648  {
4649  css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
4650  if (xLangSrv.is())
4651  {
4652  css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
4653  if (xSpell.is())
4654  aLocales = xSpell->getLocales();
4655  }
4656  }
4657 
4658  boost::property_tree::ptree aTree;
4659  aTree.put("commandName", pCommand);
4660  boost::property_tree::ptree aValues;
4661  boost::property_tree::ptree aChild;
4662  OUString sLanguage;
4663  for ( css::lang::Locale const & locale : std::as_const(aLocales) )
4664  {
4665  const LanguageTag aLanguageTag( locale );
4666  sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
4667  if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
4668  continue;
4669 
4670  sLanguage += ";" + aLanguageTag.getBcp47(false);
4671  aChild.put("", sLanguage.toUtf8());
4672  aValues.push_back(std::make_pair("", aChild));
4673  }
4674  aTree.add_child("commandValues", aValues);
4675  std::stringstream aStream;
4676  boost::property_tree::write_json(aStream, aTree);
4677  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4678  assert(pJson); // Don't handle OOM conditions
4679  strcpy(pJson, aStream.str().c_str());
4680  pJson[aStream.str().size()] = '\0';
4681  return pJson;
4682 }
4683 
4684 static char* getFonts (const char* pCommand)
4685 {
4687  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4688  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4689  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4690 
4691  boost::property_tree::ptree aTree;
4692  aTree.put("commandName", pCommand);
4693  boost::property_tree::ptree aValues;
4694  if ( pList )
4695  {
4696  sal_uInt16 nFontCount = pList->GetFontNameCount();
4697  for (sal_uInt16 i = 0; i < nFontCount; ++i)
4698  {
4699  boost::property_tree::ptree aChildren;
4700  const FontMetric& rFontMetric = pList->GetFontName(i);
4701  const int* pAry = pList->GetSizeAry(rFontMetric);
4702  sal_uInt16 nSizeCount = 0;
4703  while (pAry[nSizeCount])
4704  {
4705  boost::property_tree::ptree aChild;
4706  aChild.put("", static_cast<float>(pAry[nSizeCount]) / 10);
4707  aChildren.push_back(std::make_pair("", aChild));
4708  nSizeCount++;
4709  }
4710  aValues.add_child(rFontMetric.GetFamilyName().toUtf8().getStr(), aChildren);
4711  }
4712  }
4713  aTree.add_child("commandValues", aValues);
4714  std::stringstream aStream;
4715  boost::property_tree::write_json(aStream, aTree);
4716  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4717  assert(pJson); // Don't handle OOM conditions
4718  strcpy(pJson, aStream.str().c_str());
4719  pJson[aStream.str().size()] = '\0';
4720  return pJson;
4721 }
4722 
4723 static char* getFontSubset (std::string_view aFontName)
4724 {
4725  OUString aFoundFont(::rtl::Uri::decode(OStringToOUString(aFontName, RTL_TEXTENCODING_UTF8), rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8));
4727  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
4728  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
4729  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
4730 
4731  boost::property_tree::ptree aTree;
4732  aTree.put("commandName", ".uno:FontSubset");
4733  boost::property_tree::ptree aValues;
4734 
4735  if ( pList && !aFoundFont.isEmpty() )
4736  {
4737  sal_uInt16 nFontCount = pList->GetFontNameCount();
4738  sal_uInt16 nItFont = 0;
4739  for (; nItFont < nFontCount; ++nItFont)
4740  {
4741  if (aFoundFont == pList->GetFontName(nItFont).GetFamilyName())
4742  {
4743  break;
4744  }
4745  }
4746 
4747  if ( nItFont < nFontCount )
4748  {
4749  FontCharMapRef xFontCharMap (new FontCharMap());
4750  auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
4751  const vcl::Font& aFont(pList->GetFontName(nItFont));
4752 
4753  aDevice->SetFont(aFont);
4754  aDevice->GetFontCharMap(xFontCharMap);
4755  SubsetMap aSubMap(xFontCharMap);
4756 
4757  for (auto const& subset : aSubMap.GetSubsetMap())
4758  {
4759  boost::property_tree::ptree aChild;
4760  aChild.put("", static_cast<int>(ublock_getCode(subset.GetRangeMin())));
4761  aValues.push_back(std::make_pair("", aChild));
4762  }
4763  }
4764  }
4765 
4766  aTree.add_child("commandValues", aValues);
4767  std::stringstream aStream;
4768  boost::property_tree::write_json(aStream, aTree);
4769  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4770  assert(pJson); // Don't handle OOM conditions
4771  strcpy(pJson, aStream.str().c_str());
4772  pJson[aStream.str().size()] = '\0';
4773  return pJson;
4774 }
4775 
4776 static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand)
4777 {
4778  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4779 
4780  boost::property_tree::ptree aTree;
4781  aTree.put("commandName", pCommand);
4782  uno::Reference<css::style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4783  const uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
4784  const uno::Sequence<OUString> aStyleFamilies = xStyleFamilies->getElementNames();
4785 
4786  static const std::vector<OUString> aWriterStyles =
4787  {
4788  "Text body",
4789  "Quotations",
4790  "Title",
4791  "Subtitle",
4792  "Heading 1",
4793  "Heading 2",
4794  "Heading 3"
4795  };
4796 
4797  // We need to keep a list of the default style names
4798  // in order to filter these out later when processing
4799  // the full list of styles.
4800  std::set<OUString> aDefaultStyleNames;
4801 
4802  boost::property_tree::ptree aValues;
4803  for (OUString const & sStyleFam : aStyleFamilies)
4804  {
4805  boost::property_tree::ptree aChildren;
4806  uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(sStyleFam), uno::UNO_QUERY);
4807 
4808  // Writer provides a huge number of styles, we have a list of 7 "default" styles which
4809  // should be shown in the normal dropdown, which we should add to the start of the list
4810  // to simplify their selection.
4811  if (sStyleFam == "ParagraphStyles"
4812  && doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT)
4813  {
4814  for (const OUString& rStyle: aWriterStyles)
4815  {
4816  aDefaultStyleNames.insert( rStyle );
4817 
4818  boost::property_tree::ptree aChild;
4819  aChild.put("", rStyle.toUtf8());
4820  aChildren.push_back(std::make_pair("", aChild));
4821  }
4822  }
4823 
4824  const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
4825  for (const OUString& rStyle: aStyles )
4826  {
4827  // Filter out the default styles - they are already at the top
4828  // of the list
4829  if (aDefaultStyleNames.find(rStyle) == aDefaultStyleNames.end() ||
4830  (sStyleFam != "ParagraphStyles" || doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT) )
4831  {
4832  boost::property_tree::ptree aChild;
4833  aChild.put("", rStyle.toUtf8());
4834  aChildren.push_back(std::make_pair("", aChild));
4835  }
4836  }
4837  aValues.add_child(sStyleFam.toUtf8().getStr(), aChildren);
4838  }
4839 
4840  // Header & Footer Styles
4841  {
4842  boost::property_tree::ptree aChild;
4843  boost::property_tree::ptree aChildren;
4844  static const OUStringLiteral sPageStyles(u"PageStyles");
4845  uno::Reference<beans::XPropertySet> xProperty;
4846  uno::Reference<container::XNameContainer> xContainer;
4847 
4848  if (xStyleFamilies->hasByName(sPageStyles) && (xStyleFamilies->getByName(sPageStyles) >>= xContainer))
4849  {
4850  const uno::Sequence<OUString> aSeqNames = xContainer->getElementNames();
4851  for (OUString const & sName : aSeqNames)
4852  {
4853  bool bIsPhysical;
4854  xProperty.set(xContainer->getByName(sName), uno::UNO_QUERY);
4855  if (xProperty.is() && (xProperty->getPropertyValue("IsPhysical") >>= bIsPhysical) && bIsPhysical)
4856  {
4857  OUString displayName;
4858  xProperty->getPropertyValue("DisplayName") >>= displayName;
4859  aChild.put("", displayName.toUtf8());
4860  aChildren.push_back(std::make_pair("", aChild));
4861  }
4862  }
4863  aValues.add_child("HeaderFooter", aChildren);
4864  }
4865  }
4866 
4867  {
4868  boost::property_tree::ptree aCommandList;
4869 
4870  {
4871  boost::property_tree::ptree aChild;
4872 
4873  OUString sClearFormat = SvxResId(RID_SVXSTR_CLEARFORM);
4874 
4875  boost::property_tree::ptree aName;
4876  aName.put("", sClearFormat.toUtf8());
4877  aChild.push_back(std::make_pair("text", aName));
4878 
4879  boost::property_tree::ptree aCommand;
4880  aCommand.put("", ".uno:ResetAttributes");
4881  aChild.push_back(std::make_pair("id", aCommand));
4882 
4883  aCommandList.push_back(std::make_pair("", aChild));
4884  }
4885 
4886  aValues.add_child("Commands", aCommandList);
4887  }
4888 
4889  aTree.add_child("commandValues", aValues);
4890  std::stringstream aStream;
4891  boost::property_tree::write_json(aStream, aTree);
4892  char* pJson = static_cast<char*>(malloc(aStream.str().size() + 1));
4893  assert(pJson); // Don't handle OOM conditions
4894  strcpy(pJson, aStream.str().c_str());
4895  pJson[aStream.str().size()] = '\0';
4896  return pJson;
4897 }
4898 
4899 namespace {
4900 
4901 enum class UndoOrRedo
4902 {
4903  UNDO,
4904  REDO
4905 };
4906 
4907 }
4908 
4910 static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand)
4911 {
4912  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4913 
4914  auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
4915  if (!pBaseModel)
4916  return nullptr;
4917 
4918  SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
4919  if (!pObjectShell)
4920  return nullptr;
4921 
4922  SfxUndoManager* pUndoManager = pObjectShell->GetUndoManager();
4923  if (!pUndoManager)
4924  return nullptr;
4925 
4926  OUString aString;
4927  if (eCommand == UndoOrRedo::UNDO)
4928  aString = pUndoManager->GetUndoActionsInfo();
4929  else
4930  aString = pUndoManager->GetRedoActionsInfo();
4931  char* pJson = strdup(aString.toUtf8().getStr());
4932  return pJson;
4933 }
4934 
4936 static char* getTrackedChanges(LibreOfficeKitDocument* pThis)
4937 {
4938  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
4939 
4940  uno::Reference<document::XRedlinesSupplier> xRedlinesSupplier(pDocument->mxComponent, uno::UNO_QUERY);
4941  tools::JsonWriter aJson;
4942  // We want positions of the track changes also which is not possible from
4943  // UNO. Enable positioning information for text documents only for now, so
4944  // construct the tracked changes JSON from inside the sw/, not here using UNO
4945  if (doc_getDocumentType(pThis) != LOK_DOCTYPE_TEXT && xRedlinesSupplier.is())
4946  {
4947  auto redlinesNode = aJson.startArray("redlines");
4948  uno::Reference<container::XEnumeration> xRedlines = xRedlinesSupplier->getRedlines()->createEnumeration();
4949  for (size_t nIndex = 0; xRedlines->hasMoreElements(); ++nIndex)
4950  {
4951  uno::Reference<beans::XPropertySet> xRedline(xRedlines->nextElement(), uno::UNO_QUERY);
4952  auto redlineNode = aJson.startStruct();
4953  aJson.put("index", static_cast<sal_Int32>(nIndex));
4954 
4955  OUString sAuthor;
4956  xRedline->getPropertyValue("RedlineAuthor") >>= sAuthor;
4957  aJson.put("author", sAuthor);
4958 
4959  OUString sType;
4960  xRedline->getPropertyValue("RedlineType") >>= sType;
4961  aJson.put("type", sType);
4962 
4963  OUString sComment;
4964  xRedline->getPropertyValue("RedlineComment") >>= sComment;
4965  aJson.put("comment", sComment);
4966 
4967  OUString sDescription;
4968  xRedline->getPropertyValue("RedlineDescription") >>= sDescription;
4969  aJson.put("description", sDescription);
4970 
4971  util::DateTime aDateTime;
4972  xRedline->getPropertyValue("RedlineDateTime") >>= aDateTime;
4973  OUString sDateTime = utl::toISO8601(aDateTime);
4974  aJson.put("dateTime", sDateTime);
4975  }
4976  }
4977  else
4978  {
4979  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4980  if (!pDoc)
4981  {
4982  SetLastExceptionMsg("Document doesn't support tiled rendering");
4983  return nullptr;
4984  }
4985  pDoc->getTrackedChanges(aJson);
4986  }
4987 
4988  return aJson.extractData();
4989 }
4990 
4991 
4993 static char* getTrackedChangeAuthors(LibreOfficeKitDocument* pThis)
4994 {
4995  ITiledRenderable* pDoc = getTiledRenderable(pThis);
4996  if (!pDoc)
4997  {
4998  SetLastExceptionMsg("Document doesn't support tiled rendering");
4999  return nullptr;
5000  }
5001  tools::JsonWriter aJsonWriter;
5002  pDoc->getTrackedChangeAuthors(aJsonWriter);
5003  return aJsonWriter.extractData();
5004 }
5005 
5006 static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand)
5007 {
5008  comphelper::ProfileZone aZone("doc_getCommandValues");
5009 
5010  SolarMutexGuard aGuard;
5012 
5013  OString aCommand(pCommand);
5014  static constexpr OStringLiteral aViewRowColumnHeaders(".uno:ViewRowColumnHeaders");
5015  static constexpr OStringLiteral aSheetGeometryData(".uno:SheetGeometryData");
5016  static constexpr OStringLiteral aCellCursor(".uno:CellCursor");
5017  static constexpr OStringLiteral aFontSubset(".uno:FontSubset&name=");
5018 
5019  if (!strcmp(pCommand, ".uno:LanguageStatus"))
5020  {
5021  return getLanguages(pCommand);
5022  }
5023  else if (!strcmp(pCommand, ".uno:CharFontName"))
5024  {
5025  return getFonts(pCommand);
5026  }
5027  else if (!strcmp(pCommand, ".uno:StyleApply"))
5028  {
5029  return getStyles(pThis, pCommand);
5030  }
5031  else if (aCommand == ".uno:Undo")
5032  {
5033  return getUndoOrRedo(pThis, UndoOrRedo::UNDO);
5034  }
5035  else if (aCommand == ".uno:Redo")
5036  {
5037  return getUndoOrRedo(pThis, UndoOrRedo::REDO);
5038  }
5039  else if (aCommand == ".uno:AcceptTrackedChanges")
5040  {
5041  return getTrackedChanges(pThis);
5042  }
5043  else if (aCommand == ".uno:TrackedChangeAuthors")
5044  {
5045  return getTrackedChangeAuthors(pThis);
5046  }
5047  else if (aCommand == ".uno:ViewAnnotations")
5048  {
5049  return getPostIts(pThis);
5050  }
5051  else if (aCommand == ".uno:ViewAnnotationsPosition")
5052  {
5053  return getPostItsPos(pThis);
5054  }
5055  else if (aCommand == ".uno:RulerState")
5056  {
5057  return getRulerState(pThis);
5058  }
5059  else if (aCommand.startsWith(aViewRowColumnHeaders))
5060  {
5061  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5062  if (!pDoc)
5063  {
5064  SetLastExceptionMsg("Document doesn't support tiled rendering");
5065  return nullptr;
5066  }
5067 
5068  tools::Rectangle aRectangle;
5069  if (aCommand.getLength() > aViewRowColumnHeaders.getLength())
5070  {
5071  // Command has parameters.
5072  int nX = 0;
5073  int nY = 0;
5074  int nWidth = 0;
5075  int nHeight = 0;
5076  OString aArguments = aCommand.copy(aViewRowColumnHeaders.getLength() + 1);
5077  sal_Int32 nParamIndex = 0;
5078  do
5079  {
5080  OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
5081  sal_Int32 nIndex = 0;
5082  OString aKey;
5083  OString aValue;
5084  do
5085  {
5086  OString aToken = aParamToken.getToken(0, '=', nIndex);
5087  if (!aKey.getLength())
5088  aKey = aToken;
5089  else
5090  aValue = aToken;
5091  }
5092  while (nIndex >= 0);
5093  if (aKey == "x")
5094  nX = aValue.toInt32();
5095  else if (aKey == "y")
5096  nY = aValue.toInt32();
5097  else if (aKey == "width")
5098  nWidth = aValue.toInt32();
5099  else if (aKey == "height")
5100  nHeight = aValue.toInt32();
5101  }
5102  while (nParamIndex >= 0);
5103 
5104  aRectangle = tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight);
5105  }
5106 
5107  tools::JsonWriter aJsonWriter;
5108  pDoc->getRowColumnHeaders(aRectangle, aJsonWriter);
5109  return aJsonWriter.extractData();
5110  }
5111  else if (aCommand.startsWith(aSheetGeometryData))
5112  {
5113  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5114  if (!pDoc)
5115  {
5116  SetLastExceptionMsg("Document doesn't support tiled rendering");
5117  return nullptr;
5118  }
5119 
5120  bool bColumns = true;
5121  bool bRows = true;
5122  bool bSizes = true;
5123  bool bHidden = true;
5124  bool bFiltered = true;
5125  bool bGroups = true;
5126  if (aCommand.getLength() > aSheetGeometryData.getLength())
5127  {
5128  bColumns = bRows = bSizes = bHidden = bFiltered = bGroups = false;
5129 
5130  OString aArguments = aCommand.copy(aSheetGeometryData.getLength() + 1);
5131  sal_Int32 nParamIndex = 0;
5132  do
5133  {
5134  OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
5135  sal_Int32 nIndex = 0;
5136  OString aKey;
5137  OString aValue;
5138  do
5139  {
5140  OString aToken = aParamToken.getToken(0, '=', nIndex);
5141  if (!aKey.getLength())
5142  aKey = aToken;
5143  else
5144  aValue = aToken;
5145 
5146  } while (nIndex >= 0);
5147 
5148  bool bEnableFlag = aValue.isEmpty() ||
5149  aValue.equalsIgnoreAsciiCase("true") || aValue.toInt32() > 0;
5150  if (!bEnableFlag)
5151  continue;
5152 
5153  if (aKey == "columns")
5154  bColumns = true;
5155  else if (aKey == "rows")
5156  bRows = true;
5157  else if (aKey == "sizes")
5158  bSizes = true;
5159  else if (aKey == "hidden")
5160  bHidden = true;
5161  else if (aKey == "filtered")
5162  bFiltered = true;
5163  else if (aKey == "groups")
5164  bGroups = true;
5165 
5166  } while (nParamIndex >= 0);
5167  }
5168 
5169  OString aGeomDataStr
5170  = pDoc->getSheetGeometryData(bColumns, bRows, bSizes, bHidden, bFiltered, bGroups);
5171 
5172  if (aGeomDataStr.isEmpty())
5173  return nullptr;
5174 
5175  return convertOString(aGeomDataStr);
5176  }
5177  else if (aCommand.startsWith(aCellCursor))
5178  {
5179  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5180  if (!pDoc)
5181  {
5182  SetLastExceptionMsg("Document doesn't support tiled rendering");
5183  return nullptr;
5184  }
5185  // Ignore command's deprecated parameters.
5186  tools::JsonWriter aJsonWriter;
5187  pDoc->getCellCursor(aJsonWriter);
5188  return aJsonWriter.extractData();
5189  }
5190  else if (aCommand.startsWith(aFontSubset))
5191  {
5192  return getFontSubset(std::string_view(pCommand + aFontSubset.getLength()));
5193  }
5194  else
5195  {
5196  SetLastExceptionMsg("Unknown command, no values returned");
5197  return nullptr;
5198  }
5199 }
5200 
5201 static void doc_setClientZoom(LibreOfficeKitDocument* pThis, int nTilePixelWidth, int nTilePixelHeight,
5202  int nTileTwipWidth, int nTileTwipHeight)
5203 {
5204  comphelper::ProfileZone aZone("doc_setClientZoom");
5205 
5206  SolarMutexGuard aGuard;
5208 
5209  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5210  if (!pDoc)
5211  {
5212  SetLastExceptionMsg("Document doesn't support tiled rendering");
5213  return;
5214  }
5215 
5216  pDoc->setClientZoom(nTilePixelWidth, nTilePixelHeight, nTileTwipWidth, nTileTwipHeight);
5217 }
5218 
5219 static void doc_setClientVisibleArea(LibreOfficeKitDocument* pThis, int nX, int nY, int nWidth, int nHeight)
5220 {
5221  comphelper::ProfileZone aZone("doc_setClientVisibleArea");
5222 
5223  SolarMutexGuard aGuard;
5225 
5226  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5227  if (!pDoc)
5228  {
5229  SetLastExceptionMsg("Document doesn't support tiled rendering");
5230  return;
5231  }
5232 
5233  tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
5234  pDoc->setClientVisibleArea(aRectangle);
5235 }
5236 
5237 static void doc_setOutlineState(LibreOfficeKitDocument* pThis, bool bColumn, int nLevel, int nIndex, bool bHidden)
5238 {
5239  comphelper::ProfileZone aZone("doc_setOutlineState");
5240 
5241  SolarMutexGuard aGuard;
5243 
5244  ITiledRenderable* pDoc = getTiledRenderable(pThis);
5245  if (!pDoc)
5246  {
5247  SetLastExceptionMsg("Document doesn't support tiled rendering");
5248  return;
5249  }
5250 
5251  pDoc->setOutlineState(bColumn, nLevel, nIndex, bHidden);
5252 }
5253 
5254 static int doc_createViewWithOptions(LibreOfficeKitDocument* pThis,
5255  const char* pOptions)
5256 {
5257  comphelper::ProfileZone aZone("doc_createView");
5258 
5259  SolarMutexGuard aGuard;
5261 
5262  OUString aOptions = getUString(pOptions);
5263  const OUString aLanguage = extractParameter(aOptions, u"Language");
5264 
5265  if (!aLanguage.isEmpty())
5266  {
5267  // Set the LOK language tag, used for dialog tunneling.
5270  }
5271 
5272  const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
5273  SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
5274 
5275  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5276  const int nId = SfxLokHelper::createView(pDocument->mnDocumentId);
5277 
5279 
5280 #ifdef IOS
5281  (void) pThis;
5282 #else
5283  forceSetClipboardForCurrentView(pThis);
5284 #endif
5285 
5286  return nId;
5287 }
5288 
5289 static int doc_createView(LibreOfficeKitDocument* pThis)
5290 {
5291  return doc_createViewWithOptions(pThis, nullptr); // No options.
5292 }
5293 
5294 static void doc_destroyView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int nId)
5295 {
5296  comphelper::ProfileZone aZone("doc_destroyView");
5297 
5298  SolarMutexGuard aGuard;
5300 
5302 
5304 
5305  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5307 }
5308 
5309 static void doc_setView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId)
5310 {
5311  comphelper::ProfileZone aZone("doc_setView");
5312 
5313  SolarMutexGuard aGuard;
5315 
5316  SfxLokHelper::setView(nId);
5317 }
5318 
5319 static int doc_getView(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/)
5320 {
5321  comphelper::ProfileZone aZone("doc_getView");
5322 
5323  SolarMutexGuard aGuard;
5325 
5326  return SfxLokHelper::getView();
5327 }
5328 
5329 static int doc_getViewsCount(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis)
5330 {
5331  comphelper::ProfileZone aZone("doc_getViewsCount");
5332 
5333  SolarMutexGuard aGuard;
5335 
5336  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5337  return SfxLokHelper::getViewsCount(pDocument->mnDocumentId);
5338 }
5339 
5340 static bool doc_getViewIds(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* pThis, int* pArray, size_t nSize)
5341 {
5342  comphelper::ProfileZone aZone("doc_getViewsIds");
5343 
5344  SolarMutexGuard aGuard;
5346 
5347  LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
5348  return SfxLokHelper::getViewIds(pDocument->mnDocumentId, pArray, nSize);
5349 }
5350 
5351 static void doc_setViewLanguage(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId, const char* language)
5352 {
5353  comphelper::ProfileZone aZone("doc_setViewLanguage");
5354 
5355  SolarMutexGuard aGuard;
5357 
5358  OUString sLanguage = OStringToOUString(language, RTL_TEXTENCODING_UTF8);
5359  SfxLokHelper::setViewLanguage(nId, sLanguage);
5360  SfxLokHelper::setViewLocale(nId, sLanguage);
5361 }
5362 
5363 
5364 
5365 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
5366  const char* pFontName,
5367  const char* pChar,
5368  int* pFontWidth,
5369  int* pFontHeight)
5370 {
5371  return doc_renderFontOrientation(pThis, pFontName, pChar, pFontWidth, pFontHeight, 0);
5372 }
5373 
5374 unsigned char* doc_renderFontOrientation(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/,
5375  const char* pFontName,
5376  const char* pChar,
5377  int* pFontWidth,
5378  int* pFontHeight,
5379  int pOrientation)
5380 {
5381  comphelper::ProfileZone aZone("doc_renderFont");
5382 
5383  SolarMutexGuard aGuard;
5385 
5386  OString aSearchedFontName(pFontName);
5387  OUString aText(OStringToOUString(pChar, RTL_TEXTENCODING_UTF8));
5389  const SvxFontListItem* pFonts = static_cast<const SvxFontListItem*>(
5390  pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
5391  const FontList* pList = pFonts ? pFonts->GetFontList() : nullptr;
5392 
5393  const int nDefaultFontSize = 25;
5394 
5395  if ( pList )
5396  {
5397  sal_uInt16 nFontCount = pList->GetFontNameCount();
5398  for (sal_uInt16 i = 0; i < nFontCount; ++i)
5399  {
5400  const FontMetric& rFontMetric = pList->GetFontName(i);
5401  const OUString& aFontName = rFontMetric.GetFamilyName();
5402  if (aSearchedFontName != aFontName.toUtf8())
5403  continue;
5404 
5405  if (aText.isEmpty())
5406  aText = rFontMetric.GetFamilyName();
5407 
5408  auto aDevice(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
5409  ::tools::Rectangle aRect;
5410  vcl::Font aFont(rFontMetric);
5411  aFont.SetFontSize(Size(0, nDefaultFontSize));