Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / edit / edfcol.cxx
blob18e0055c9324739ac50b5fef6730cbc64fd352be
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <editsh.hxx>
26 #include <com/sun/star/container/XEnumerationAccess.hpp>
27 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
28 #include <com/sun/star/document/XActionLockable.hpp>
29 #include <com/sun/star/document/XDocumentProperties.hpp>
30 #include <com/sun/star/drawing/FillStyle.hpp>
31 #include <com/sun/star/drawing/HomogenMatrix3.hpp>
32 #include <com/sun/star/drawing/LineStyle.hpp>
33 #include <com/sun/star/drawing/XShape.hpp>
34 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
35 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
36 #include <com/sun/star/lang/XServiceInfo.hpp>
37 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
38 #include <com/sun/star/text/RelOrientation.hpp>
39 #include <com/sun/star/text/TextContentAnchorType.hpp>
40 #include <com/sun/star/text/VertOrientation.hpp>
41 #include <com/sun/star/text/WrapTextMode.hpp>
42 #include <com/sun/star/text/XTextContent.hpp>
43 #include <com/sun/star/text/XTextDocument.hpp>
44 #include <com/sun/star/text/XTextField.hpp>
45 #include <com/sun/star/text/XTextRange.hpp>
46 #include <com/sun/star/text/XParagraphAppend.hpp>
47 #include <com/sun/star/text/XParagraphCursor.hpp>
48 #include <com/sun/star/awt/FontWeight.hpp>
49 #include <com/sun/star/rdf/XMetadatable.hpp>
50 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
51 #include <com/sun/star/security/XCertificate.hpp>
53 #include <basegfx/matrix/b2dhommatrix.hxx>
54 #include <comphelper/propertysequence.hxx>
55 #include <comphelper/propertyvalue.hxx>
56 #include <comphelper/processfactory.hxx>
57 #include <comphelper/sequence.hxx>
58 #include <comphelper/scopeguard.hxx>
59 #include <comphelper/string.hxx>
60 #include <editeng/unoprnms.hxx>
61 #include <sfx2/classificationhelper.hxx>
62 #include <svx/ClassificationCommon.hxx>
63 #include <svx/ClassificationField.hxx>
64 #include <svl/cryptosign.hxx>
65 #include <svl/sigstruct.hxx>
66 #include <utility>
67 #include <vcl/svapp.hxx>
68 #include <vcl/weld.hxx>
69 #include <vcl/virdev.hxx>
71 #include <redline.hxx>
72 #include <poolfmt.hxx>
73 #include <hintids.hxx>
74 #include <doc.hxx>
75 #include <IDocumentUndoRedo.hxx>
76 #include <ndtxt.hxx>
77 #include <paratr.hxx>
78 #include <viewopt.hxx>
79 #include <SwRewriter.hxx>
80 #include <numrule.hxx>
81 #include <swundo.hxx>
82 #include <docary.hxx>
83 #include <docsh.hxx>
84 #include <unoprnms.hxx>
85 #include <rootfrm.hxx>
86 #include <pagefrm.hxx>
87 #include <txtfrm.hxx>
88 #include <rdfhelper.hxx>
89 #include <sfx2/watermarkitem.hxx>
91 #include <unoparagraph.hxx>
92 #include <strings.hrc>
93 #include <undobj.hxx>
94 #include <UndoParagraphSignature.hxx>
95 #include <txtatr.hxx>
96 #include <fmtmeta.hxx>
97 #include <unotxdoc.hxx>
98 #include <unotextbodyhf.hxx>
99 #include <unoport.hxx>
101 #include <comphelper/diagnose_ex.hxx>
102 #include <IDocumentRedlineAccess.hxx>
104 constexpr OUStringLiteral WATERMARK_NAME = u"PowerPlusWaterMarkObject";
105 #define WATERMARK_AUTO_SIZE sal_uInt32(1)
107 namespace
109 constexpr OUStringLiteral MetaFilename(u"tscp/bails.rdf");
110 constexpr OUStringLiteral MetaNS(u"urn:bails");
111 constexpr OUStringLiteral ParagraphSignatureRDFNamespace = u"urn:bails:loext:paragraph:signature:";
112 constexpr OUStringLiteral ParagraphSignatureIdRDFName = u"urn:bails:loext:paragraph:signature:id";
113 constexpr OUStringLiteral ParagraphSignatureDigestRDFName = u":digest";
114 constexpr OUStringLiteral ParagraphSignatureDateRDFName = u":date";
115 constexpr OUStringLiteral ParagraphSignatureUsageRDFName = u":usage";
116 constexpr OUStringLiteral ParagraphSignatureLastIdRDFName = u"urn:bails:loext:paragraph:signature:lastid";
117 constexpr OUStringLiteral ParagraphClassificationNameRDFName = u"urn:bails:loext:paragraph:classification:name";
118 constexpr OUStringLiteral ParagraphClassificationValueRDFName = u"urn:bails:loext:paragraph:classification:value";
119 constexpr OUStringLiteral ParagraphClassificationAbbrRDFName = u"urn:bails:loext:paragraph:classification:abbreviation";
120 constexpr OUStringLiteral ParagraphClassificationFieldNamesRDFName = u"urn:bails:loext:paragraph:classification:fields";
121 constexpr OUStringLiteral MetadataFieldServiceName = u"com.sun.star.text.textfield.MetadataField";
122 constexpr OUStringLiteral DocInfoServiceName = u"com.sun.star.text.TextField.DocInfo.Custom";
124 /// Find all page styles which are currently used in the document.
125 std::vector<OUString> lcl_getUsedPageStyles(SwViewShell const * pShell)
127 std::vector<OUString> aReturn;
129 SwRootFrame* pLayout = pShell->GetLayout();
130 for (SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
132 SwPageFrame* pPage = static_cast<SwPageFrame*>(pFrame);
133 if (const SwPageDesc *pDesc = pPage->FindPageDesc())
134 aReturn.push_back(pDesc->GetName());
137 return aReturn;
140 /// Search for a field named rFieldName of type rServiceName in xText and return it.
141 uno::Reference<text::XTextField> lcl_findField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, std::u16string_view rFieldName)
143 uno::Reference<text::XTextField> xField;
144 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
145 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
146 while (xParagraphs->hasMoreElements())
148 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
149 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
150 while (xTextPortions->hasMoreElements())
152 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
153 OUString aTextPortionType;
154 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
155 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
156 continue;
158 uno::Reference<lang::XServiceInfo> xTextField;
159 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
160 if (!xTextField->supportsService(rServiceName))
161 continue;
163 OUString aName;
164 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
165 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
166 if (aName == rFieldName)
168 xField = uno::Reference<text::XTextField>(xTextField, uno::UNO_QUERY);
169 break;
174 return xField;
177 /// Search for a field named rFieldName of type rServiceName in xText and return true iff found.
178 bool lcl_hasField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, std::u16string_view rFieldName)
180 return lcl_findField(xText, rServiceName, rFieldName).is();
183 /// Search for a frame with WATERMARK_NAME in name of type rServiceName in xText. Returns found name in rShapeName.
184 uno::Reference<drawing::XShape> lcl_getWatermark(const uno::Reference<text::XText>& xText,
185 const OUString& rServiceName, OUString& rShapeName, bool& bSuccess)
187 bSuccess = false;
188 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
189 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
190 while (xParagraphs->hasMoreElements())
192 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
193 if (!xTextPortionEnumerationAccess.is())
194 continue;
196 bSuccess = true;
198 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
199 while (xTextPortions->hasMoreElements())
201 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
202 OUString aTextPortionType;
203 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
204 if (aTextPortionType != "Frame")
205 continue;
207 uno::Reference<container::XContentEnumerationAccess> xContentEnumerationAccess(xTextPortion, uno::UNO_QUERY);
208 if (!xContentEnumerationAccess.is())
209 continue;
211 uno::Reference<container::XEnumeration> xEnumeration = xContentEnumerationAccess->createContentEnumeration("com.sun.star.text.TextContent");
212 if (!xEnumeration->hasMoreElements())
213 continue;
215 uno::Reference<lang::XServiceInfo> xWatermark(xEnumeration->nextElement(), uno::UNO_QUERY);
216 if (!xWatermark->supportsService(rServiceName))
217 continue;
219 uno::Reference<container::XNamed> xNamed(xWatermark, uno::UNO_QUERY);
221 if (!xNamed->getName().match(WATERMARK_NAME))
222 continue;
224 rShapeName = xNamed->getName();
226 uno::Reference<drawing::XShape> xShape(xWatermark, uno::UNO_QUERY);
227 return xShape;
231 return uno::Reference<drawing::XShape>();
234 /// Extract the text of the paragraph without any of the fields.
235 /// TODO: Consider moving to SwTextNode, or extend ModelToViewHelper.
236 OString lcl_getParagraphBodyText(const uno::Reference<text::XTextContent>& xText)
238 OUStringBuffer strBuf;
239 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xText, uno::UNO_QUERY);
240 if (!xTextPortionEnumerationAccess.is())
241 return OString();
243 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
244 while (xTextPortions->hasMoreElements())
246 uno::Any elem = xTextPortions->nextElement();
248 //TODO: Consider including hidden and conditional texts/portions.
249 OUString aTextPortionType;
250 uno::Reference<beans::XPropertySet> xPropertySet(elem, uno::UNO_QUERY);
251 xPropertySet->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
252 if (aTextPortionType == "Text")
254 uno::Reference<text::XTextRange> xTextRange(elem, uno::UNO_QUERY);
255 if (xTextRange.is())
256 strBuf.append(xTextRange->getString());
260 // Cleanup the dummy characters added by fields (which we exclude).
261 comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDSTART);
262 comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDEND);
263 comphelper::string::remove(strBuf, CH_TXTATR_BREAKWORD);
265 return strBuf.makeStringAndClear().trim().toUtf8();
268 template <typename T>
269 std::map<OUString, OUString> lcl_getRDFStatements(const uno::Reference<frame::XModel>& xModel,
270 const T& xRef)
274 const css::uno::Reference<css::rdf::XResource> xSubject(xRef, uno::UNO_QUERY);
275 return SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
277 catch (const ::css::uno::Exception&)
281 return std::map<OUString, OUString>();
284 /// Returns RDF (key, value) pair associated with the field, if any.
285 std::pair<OUString, OUString> lcl_getFieldRDFByPrefix(const uno::Reference<frame::XModel>& xModel,
286 const uno::Reference<css::text::XTextField>& xField,
287 std::u16string_view sPrefix)
289 for (const auto& pair : lcl_getRDFStatements(xModel, xField))
291 if (pair.first.startsWith(sPrefix))
292 return pair;
295 return std::make_pair(OUString(), OUString());
298 /// Returns RDF (key, value) pair associated with the field, if any.
299 template <typename T>
300 std::pair<OUString, OUString> lcl_getRDF(const uno::Reference<frame::XModel>& xModel,
301 const T& xRef,
302 const OUString& sRDFName)
304 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xRef);
305 const auto it = aStatements.find(sRDFName);
306 return (it != aStatements.end()) ? std::make_pair(it->first, it->second) : std::make_pair(OUString(), OUString());
309 /// Returns true iff the field in question is paragraph signature.
310 /// Note: must have associated RDF, since signatures are otherwise just metadata fields.
311 bool lcl_IsParagraphSignatureField(const uno::Reference<frame::XModel>& xModel,
312 const uno::Reference<css::text::XTextField>& xField)
314 return (lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).first == ParagraphSignatureIdRDFName);
317 uno::Reference<text::XTextField> lcl_findFieldByRDF(const uno::Reference<frame::XModel>& xModel,
318 const uno::Reference<text::XTextContent>& xParagraph,
319 const OUString& sRDFName,
320 std::u16string_view sRDFValue)
322 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
323 if (!xTextPortionEnumerationAccess.is())
324 return uno::Reference<text::XTextField>();
326 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
327 if (!xTextPortions.is())
328 return uno::Reference<text::XTextField>();
330 while (xTextPortions->hasMoreElements())
332 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
333 OUString aTextPortionType;
334 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
335 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
336 continue;
338 uno::Reference<lang::XServiceInfo> xTextField;
339 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
340 if (!xTextField->supportsService(MetadataFieldServiceName))
341 continue;
343 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
344 const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xField, sRDFName);
345 if (pair.first == sRDFName && (sRDFValue.empty() || sRDFValue == pair.second))
346 return xField;
349 return uno::Reference<text::XTextField>();
352 struct SignatureDescr
354 OUString msSignature;
355 OUString msUsage;
356 OUString msDate;
358 bool isValid() const { return !msSignature.isEmpty(); }
361 SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
362 const uno::Reference<css::text::XTextContent>& xParagraph,
363 std::u16string_view sFieldId)
365 SignatureDescr aDescr;
367 const OUString prefix = ParagraphSignatureRDFNamespace + sFieldId;
368 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xParagraph);
370 const auto itSig = aStatements.find(prefix + ParagraphSignatureDigestRDFName);
371 aDescr.msSignature = (itSig != aStatements.end() ? itSig->second : OUString());
373 const auto itDate = aStatements.find(prefix + ParagraphSignatureDateRDFName);
374 aDescr.msDate = (itDate != aStatements.end() ? itDate->second : OUString());
376 const auto itUsage = aStatements.find(prefix + ParagraphSignatureUsageRDFName);
377 aDescr.msUsage = (itUsage != aStatements.end() ? itUsage->second : OUString());
379 return aDescr;
382 SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
383 const uno::Reference<css::text::XTextContent>& xParagraph,
384 const uno::Reference<css::text::XTextField>& xField)
386 const OUString sFieldId = lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).second;
387 if (!sFieldId.isEmpty())
388 return lcl_getSignatureDescr(xModel, xParagraph, sFieldId);
390 return SignatureDescr();
393 /// Validate and create the signature field display text from the fields.
394 std::pair<bool, OUString> lcl_MakeParagraphSignatureFieldText(const SignatureDescr& aDescr,
395 const OString& utf8Text)
397 OUString msg = SwResId(STR_INVALID_SIGNATURE);
398 bool valid = false;
400 if (aDescr.isValid())
402 const char* pData = utf8Text.getStr();
403 const std::vector<unsigned char> data(pData, pData + utf8Text.getLength());
405 OString encSignature;
406 if (aDescr.msSignature.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
408 const std::vector<unsigned char> sig(svl::crypto::DecodeHexString(encSignature));
409 SignatureInformation aInfo(0);
410 valid = svl::crypto::Signing::Verify(data, false, sig, aInfo);
411 valid = valid
412 && aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
414 assert(aInfo.GetSigningCertificate()); // it was valid
415 msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " +
416 aDescr.msDate;
417 msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": "));
418 msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID));
422 return std::make_pair(valid, msg);
425 /// Validate and return validation result and signature field display text.
426 std::pair<bool, OUString>
427 lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
428 const uno::Reference<css::text::XTextContent>& xParagraph,
429 const uno::Reference<css::text::XTextField>& xField,
430 const OString& utf8Text)
432 const SignatureDescr aDescr = lcl_getSignatureDescr(xModel, xParagraph, xField);
433 return lcl_MakeParagraphSignatureFieldText(aDescr, utf8Text);
436 /// Generate the next valid ID for the new signature on this paragraph.
437 OUString lcl_getNextSignatureId(const uno::Reference<frame::XModel>& xModel,
438 const uno::Reference<text::XTextContent>& xParagraph)
440 const OUString sFieldId = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName).second;
441 return OUString::number(!sFieldId.isEmpty() ? sFieldId.toInt32() + 1 : 1);
444 /// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
445 uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Reference<frame::XModel>& xModel,
446 const uno::Reference<text::XTextContent>& xParagraph,
447 const OUString& signature,
448 const OUString& usage)
450 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
451 auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
453 // Add the signature at the end.
454 xField->attach(xParagraph->getAnchor()->getEnd());
456 const OUString sId = lcl_getNextSignatureId(xModel, xParagraph);
458 const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
459 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureIdRDFName, sId);
461 // First convert the UTC UNIX timestamp to a tools::DateTime then to local time.
462 DateTime aDateTime = DateTime::CreateFromUnixTime(time(nullptr));
463 aDateTime.ConvertToLocalTime();
464 OUStringBuffer rBuffer;
465 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetYear()));
466 rBuffer.append('-');
467 if (aDateTime.GetMonth() < 10)
468 rBuffer.append('0');
469 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
470 rBuffer.append('-');
471 if (aDateTime.GetDay() < 10)
472 rBuffer.append('0');
473 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetDay()));
475 // Now set the RDF on the paragraph, since that's what is preserved in .doc(x).
476 const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
477 const OUString prefix = ParagraphSignatureRDFNamespace + sId;
478 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureLastIdRDFName, sId);
479 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDigestRDFName, signature);
480 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureUsageRDFName, usage);
481 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDateRDFName, rBuffer.makeStringAndClear());
483 return xField;
486 /// Updates the signature field text if changed and returns true only iff updated.
487 bool lcl_DoUpdateParagraphSignatureField(SwDoc& rDoc,
488 const uno::Reference<css::text::XTextField>& xField,
489 const OUString& sDisplayText)
491 // Disable undo to avoid introducing noise when we edit the metadata field.
492 const bool isUndoEnabled = rDoc.GetIDocumentUndoRedo().DoesUndo();
493 rDoc.GetIDocumentUndoRedo().DoUndo(false);
494 comphelper::ScopeGuard const g([&rDoc, isUndoEnabled]() {
495 rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
500 uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
501 const OUString curText = xText->getString();
502 if (curText != sDisplayText)
504 xText->setString(sDisplayText);
505 return true;
508 catch (const uno::Exception&)
510 // We failed; avoid crashing.
511 DBG_UNHANDLED_EXCEPTION("sw.uno", "Failed to update paragraph signature");
514 return false;
517 /// Updates the signature field text if changed and returns true only iff updated.
518 bool lcl_UpdateParagraphSignatureField(SwDoc& rDoc,
519 const uno::Reference<frame::XModel>& xModel,
520 const uno::Reference<css::text::XTextContent>& xParagraph,
521 const uno::Reference<css::text::XTextField>& xField,
522 const OString& utf8Text)
524 const OUString sDisplayText
525 = lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).second;
526 return lcl_DoUpdateParagraphSignatureField(rDoc, xField, sDisplayText);
529 void lcl_RemoveParagraphMetadataField(const uno::Reference<css::text::XTextField>& xField)
531 uno::Reference<css::text::XTextRange> xParagraph(xField->getAnchor());
532 xParagraph->getText()->removeTextContent(xField);
535 /// Returns true iff the field in question is paragraph classification.
536 /// Note: must have associated RDF, since classifications are otherwise just metadata fields.
537 bool lcl_IsParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
538 const uno::Reference<css::text::XTextField>& xField,
539 std::u16string_view sKey)
541 const std::pair<OUString, OUString> rdfPair = lcl_getRDF(xModel, xField, ParagraphClassificationNameRDFName);
542 return rdfPair.first == ParagraphClassificationNameRDFName && (sKey.empty() || rdfPair.second == sKey);
545 uno::Reference<text::XTextField> lcl_FindParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
546 const rtl::Reference<SwXParagraph>& xParagraph,
547 std::u16string_view sKey = u"")
549 uno::Reference<text::XTextField> xTextField;
551 if (!xParagraph.is())
552 return xTextField;
554 // Enumerate text portions to find metadata fields. This is expensive, best to enumerate fields only.
555 rtl::Reference<SwXTextPortionEnumeration> xTextPortions = xParagraph->createTextFieldsEnumeration();
556 while (xTextPortions->hasMoreElements())
558 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
559 OUString aTextPortionType;
560 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
561 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
562 continue;
564 uno::Reference<lang::XServiceInfo> xServiceInfo;
565 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xServiceInfo;
566 if (!xServiceInfo->supportsService(MetadataFieldServiceName))
567 continue;
569 uno::Reference<text::XTextField> xField(xServiceInfo, uno::UNO_QUERY);
570 if (lcl_IsParagraphClassificationField(xModel, xField, sKey))
572 xTextField = xField;
573 break;
577 return xTextField;
580 /// Creates and inserts Paragraph Classification Metadata field and creates the RDF entry
581 uno::Reference<text::XTextField> lcl_InsertParagraphClassification(const uno::Reference<frame::XModel>& xModel,
582 const uno::Reference<text::XTextContent>& xParent)
584 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
585 auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
587 // Add the classification at the start.
588 xField->attach(xParent->getAnchor()->getStart());
589 return xField;
592 /// Updates the paragraph classification field text if changed and returns true only iff updated.
593 bool lcl_UpdateParagraphClassificationField(SwDoc* pDoc,
594 const uno::Reference<frame::XModel>& xModel,
595 const uno::Reference<css::text::XTextContent>& xTextNode,
596 const OUString& sKey,
597 const OUString& sValue,
598 const OUString& sDisplayText)
600 // Disable undo to avoid introducing noise when we edit the metadata field.
601 const bool isUndoEnabled = pDoc->GetIDocumentUndoRedo().DoesUndo();
602 pDoc->GetIDocumentUndoRedo().DoUndo(false);
603 comphelper::ScopeGuard const g([pDoc, isUndoEnabled] () {
604 pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
607 uno::Reference<text::XTextField> xField = lcl_InsertParagraphClassification(xModel, xTextNode);
609 css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
610 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, sKey, sValue);
611 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationNameRDFName, sKey);
612 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationValueRDFName, sValue);
614 css::uno::Reference<css::rdf::XResource> xNodeSubject(xTextNode, uno::UNO_QUERY);
615 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, sKey, sValue);
617 return lcl_DoUpdateParagraphSignatureField(*pDoc, xField, sDisplayText);
620 void lcl_ValidateParagraphSignatures(SwDoc& rDoc, const uno::Reference<text::XTextContent>& xParagraph, const bool updateDontRemove)
622 SwDocShell* pDocShell = rDoc.GetDocShell();
623 if (!pDocShell)
624 return;
626 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
628 // Check if the paragraph is signed.
631 const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName);
632 if (pair.second.isEmpty())
633 return;
635 catch (const ::css::uno::Exception&)
637 return;
640 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
641 if (!xTextPortionEnumerationAccess.is())
642 return;
644 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
645 if (!xTextPortions.is())
646 return;
648 // Get the text (without fields).
649 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
650 if (utf8Text.isEmpty())
651 return;
653 while (xTextPortions->hasMoreElements())
655 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
656 OUString aTextPortionType;
657 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
658 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
659 continue;
661 uno::Reference<lang::XServiceInfo> xTextField;
662 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
663 if (!xTextField->supportsService(MetadataFieldServiceName))
664 continue;
666 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
667 if (!lcl_IsParagraphSignatureField(xModel, xField))
669 continue;
672 if (updateDontRemove)
674 lcl_UpdateParagraphSignatureField(rDoc, xModel, xParagraph, xField, utf8Text);
676 else if (!lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).first)
678 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
679 rDoc.GetIDocumentUndoRedo().AppendUndo(
680 std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, false));
681 lcl_RemoveParagraphMetadataField(xField);
682 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
687 } // anonymous namespace
689 SwTextFormatColl& SwEditShell::GetDfltTextFormatColl() const
691 return *GetDoc()->GetDfltTextFormatColl();
694 sal_uInt16 SwEditShell::GetTextFormatCollCount() const
696 return GetDoc()->GetTextFormatColls()->size();
699 SwTextFormatColl& SwEditShell::GetTextFormatColl(sal_uInt16 nFormatColl) const
701 return *((*(GetDoc()->GetTextFormatColls()))[nFormatColl]);
704 static void insertFieldToDocument(uno::Reference<lang::XMultiServiceFactory> const & rxMultiServiceFactory,
705 uno::Reference<text::XText> const & rxText, uno::Reference<text::XParagraphCursor> const & rxParagraphCursor,
706 OUString const & rsKey)
708 uno::Reference<beans::XPropertySet> xField(rxMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
709 xField->setPropertyValue(UNO_NAME_NAME, uno::Any(rsKey));
710 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
712 rxText->insertTextContent(rxParagraphCursor, xTextContent, false);
715 static void removeAllClassificationFields(std::u16string_view rPolicy, uno::Reference<text::XText> const & rxText)
717 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(rxText, uno::UNO_QUERY);
718 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
719 while (xParagraphs->hasMoreElements())
721 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
722 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
723 while (xTextPortions->hasMoreElements())
725 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
726 OUString aTextPortionType;
727 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
728 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
729 continue;
731 uno::Reference<lang::XServiceInfo> xTextField;
732 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
733 if (!xTextField->supportsService(DocInfoServiceName))
734 continue;
736 OUString aName;
737 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
738 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
739 if (aName.startsWith(rPolicy))
741 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
742 rxText->removeTextContent(xField);
748 static sal_Int32 getNumberOfParagraphs(uno::Reference<text::XText> const & xText)
750 uno::Reference<container::XEnumerationAccess> xParagraphEnumAccess(xText, uno::UNO_QUERY);
751 uno::Reference<container::XEnumeration> xParagraphEnum = xParagraphEnumAccess->createEnumeration();
752 sal_Int32 nResult = 0;
753 while (xParagraphEnum->hasMoreElements())
755 xParagraphEnum->nextElement();
756 nResult++;
758 return nResult;
761 static void equaliseNumberOfParagraph(std::vector<svx::ClassificationResult> const & rResults, uno::Reference<text::XText> const & xText)
763 sal_Int32 nNumberOfParagraphs = 0;
764 for (svx::ClassificationResult const & rResult : rResults)
766 if (rResult.meType == svx::ClassificationType::PARAGRAPH)
767 nNumberOfParagraphs++;
770 while (getNumberOfParagraphs(xText) < nNumberOfParagraphs)
772 uno::Reference<text::XParagraphAppend> xParagraphAppend(xText, uno::UNO_QUERY);
773 xParagraphAppend->finishParagraph(uno::Sequence<beans::PropertyValue>());
777 void SwEditShell::ApplyAdvancedClassification(std::vector<svx::ClassificationResult> const & rResults)
779 SwDocShell* pDocShell = GetDoc()->GetDocShell();
780 if (!pDocShell)
781 return;
783 const SfxObjectShell* pObjSh = SfxObjectShell::Current();
784 if (!pObjSh)
785 return;
787 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
788 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
789 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
790 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
792 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
794 uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjSh->getDocProperties();
796 const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
797 const std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
798 for (const OUString& rPageStyleName : aUsedPageStyles)
800 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
802 // HEADER
803 bool bHeaderIsOn = false;
804 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
805 uno::Reference<text::XText> xHeaderText;
806 if (bHeaderIsOn)
807 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
808 if (xHeaderText.is())
809 removeAllClassificationFields(sPolicy, xHeaderText);
811 // FOOTER
812 bool bFooterIsOn = false;
813 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
814 uno::Reference<text::XText> xFooterText;
815 if (bFooterIsOn)
816 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
817 if (xFooterText.is())
818 removeAllClassificationFields(sPolicy, xFooterText);
821 // Clear properties
822 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
823 svx::classification::removeAllProperties(xPropertyContainer);
825 SfxClassificationHelper aHelper(xDocumentProperties);
827 // Apply properties from the BA policy
828 for (svx::ClassificationResult const & rResult : rResults)
830 if (rResult.meType == svx::ClassificationType::CATEGORY)
832 aHelper.SetBACName(rResult.msName, SfxClassificationHelper::getPolicyType());
836 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
838 // Insert origin document property
839 svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::MANUAL);
841 // Insert full text as document property
842 svx::classification::insertFullTextualRepresentationAsDocumentProperty(xPropertyContainer, aCreator, rResults);
844 for (const OUString& rPageStyleName : aUsedPageStyles)
846 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
848 // HEADER
849 bool bHeaderIsOn = false;
850 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
851 if (!bHeaderIsOn)
852 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
853 uno::Reference<text::XText> xHeaderText;
854 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
855 equaliseNumberOfParagraph(rResults, xHeaderText);
857 // FOOTER
858 bool bFooterIsOn = false;
859 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
860 if (!bFooterIsOn)
861 xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::Any(true));
862 uno::Reference<text::XText> xFooterText;
863 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
864 equaliseNumberOfParagraph(rResults, xFooterText);
866 // SET/DELETE WATERMARK
867 SfxWatermarkItem aWatermarkItem;
868 aWatermarkItem.SetText(aHelper.GetDocumentWatermark());
869 SetWatermark(aWatermarkItem);
871 uno::Reference<text::XParagraphCursor> xHeaderParagraphCursor(xHeaderText->createTextCursor(), uno::UNO_QUERY);
872 uno::Reference<text::XParagraphCursor> xFooterParagraphCursor(xFooterText->createTextCursor(), uno::UNO_QUERY);
874 sal_Int32 nParagraph = -1;
876 for (svx::ClassificationResult const & rResult : rResults)
878 switch(rResult.meType)
880 case svx::ClassificationType::TEXT:
882 OUString sKey = aCreator.makeNumberedTextKey();
884 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
885 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
886 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
888 break;
890 case svx::ClassificationType::CATEGORY:
892 OUString sKey = aCreator.makeCategoryNameKey();
893 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
894 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
896 break;
898 case svx::ClassificationType::MARKING:
900 OUString sKey = aCreator.makeNumberedMarkingKey();
901 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
902 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
903 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
905 break;
907 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
909 OUString sKey = aCreator.makeNumberedIntellectualPropertyPartKey();
910 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
911 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
912 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
914 break;
916 case svx::ClassificationType::PARAGRAPH:
918 nParagraph++;
920 if (nParagraph != 0) // only jump to next paragraph, if we aren't at the first paragraph
922 xHeaderParagraphCursor->gotoNextParagraph(false);
923 xFooterParagraphCursor->gotoNextParagraph(false);
926 xHeaderParagraphCursor->gotoStartOfParagraph(false);
927 xFooterParagraphCursor->gotoStartOfParagraph(false);
929 uno::Reference<beans::XPropertySet> xHeaderPropertySet(xHeaderParagraphCursor, uno::UNO_QUERY_THROW);
930 uno::Reference<beans::XPropertySet> xFooterPropertySet(xFooterParagraphCursor, uno::UNO_QUERY_THROW);
931 if (rResult.msName == "BOLD")
933 xHeaderPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::BOLD));
934 xFooterPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::BOLD));
936 else
938 xHeaderPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::NORMAL));
939 xFooterPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::NORMAL));
942 break;
944 default:
945 break;
951 std::vector<svx::ClassificationResult> SwEditShell::CollectAdvancedClassification()
953 std::vector<svx::ClassificationResult> aResult;
955 SwDocShell* pDocShell = GetDoc()->GetDocShell();
956 if (!pDocShell)
957 return aResult;
959 const SfxObjectShell* pObjSh = SfxObjectShell::Current();
960 if (!pObjSh)
961 return aResult;
963 const OUString sBlank;
965 uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjSh->getDocProperties();
966 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
967 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
969 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
970 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
971 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
972 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
974 std::vector<OUString> aPageStyles = lcl_getUsedPageStyles(this);
975 OUString aPageStyleString = aPageStyles.back();
976 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(aPageStyleString), uno::UNO_QUERY);
978 bool bHeaderIsOn = false;
979 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
980 if (!bHeaderIsOn)
982 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
983 if (!aValue.isEmpty())
984 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
986 return aResult;
989 uno::Reference<text::XText> xHeaderText;
990 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
992 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xHeaderText, uno::UNO_QUERY);
993 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
995 // set to true if category was found in the header
996 bool bFoundClassificationCategory = false;
998 while (xParagraphs->hasMoreElements())
1000 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
1001 if (!xTextPortionEnumerationAccess.is())
1002 continue;
1003 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
1005 // Check font weight
1006 uno::Reference<beans::XPropertySet> xParagraphPropertySet(xTextPortionEnumerationAccess, uno::UNO_QUERY_THROW);
1007 uno::Any aAny = xParagraphPropertySet->getPropertyValue("CharWeight");
1009 OUString sWeight = (aAny.get<float>() >= awt::FontWeight::BOLD) ? OUString("BOLD") : OUString("NORMAL");
1011 aResult.push_back({ svx::ClassificationType::PARAGRAPH, sWeight, sBlank, sBlank });
1013 // Process portions
1014 while (xTextPortions->hasMoreElements())
1016 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
1017 OUString aTextPortionType;
1018 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
1019 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
1020 continue;
1022 uno::Reference<lang::XServiceInfo> xTextField;
1023 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
1024 if (!xTextField->supportsService(DocInfoServiceName))
1025 continue;
1027 OUString aName;
1028 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
1029 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
1031 if (aCreator.isMarkingTextKey(aName))
1033 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1034 if (!aValue.isEmpty())
1035 aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
1037 else if (aCreator.isCategoryNameKey(aName))
1039 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1040 if (!aValue.isEmpty())
1041 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1042 bFoundClassificationCategory = true;
1044 else if (aCreator.isCategoryIdentifierKey(aName))
1046 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1047 if (!aValue.isEmpty())
1048 aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
1049 bFoundClassificationCategory = true;
1051 else if (aCreator.isMarkingKey(aName))
1053 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1054 if (!aValue.isEmpty())
1055 aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
1057 else if (aCreator.isIntellectualPropertyPartKey(aName))
1059 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1060 if (!aValue.isEmpty())
1061 aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, sBlank, sBlank });
1066 if (!bFoundClassificationCategory)
1068 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
1069 if (!aValue.isEmpty())
1070 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1073 return aResult;
1076 void SwEditShell::SetClassification(const OUString& rName, SfxClassificationPolicyType eType)
1078 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1079 if (!pDocShell)
1080 return;
1082 SfxClassificationHelper aHelper(pDocShell->getDocProperties());
1084 const bool bHadWatermark = !aHelper.GetDocumentWatermark().isEmpty();
1086 // This updates the infobar as well.
1087 aHelper.SetBACName(rName, eType);
1089 // Insert origin document property
1090 uno::Reference<beans::XPropertyContainer> xPropertyContainer = pDocShell->getDocProperties()->getUserDefinedProperties();
1091 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
1092 svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::BAF_POLICY);
1094 bool bHeaderIsNeeded = aHelper.HasDocumentHeader();
1095 bool bFooterIsNeeded = aHelper.HasDocumentFooter();
1096 OUString aWatermark = aHelper.GetDocumentWatermark();
1097 bool bWatermarkIsNeeded = !aWatermark.isEmpty();
1099 if (!bHeaderIsNeeded && !bFooterIsNeeded && !bWatermarkIsNeeded && !bHadWatermark)
1100 return;
1102 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1103 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1104 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1105 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1106 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
1108 for (const OUString& rPageStyleName : aStyles)
1110 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1111 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
1113 if (bHeaderIsNeeded || bWatermarkIsNeeded || bHadWatermark)
1115 // If the header is off, turn it on.
1116 bool bHeaderIsOn = false;
1117 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1118 if (!bHeaderIsOn)
1119 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
1121 // If the header already contains a document header field, no need to do anything.
1122 uno::Reference<text::XText> xHeaderText;
1123 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1125 if (bHeaderIsNeeded)
1127 if (!lcl_hasField(xHeaderText, DocInfoServiceName, Concat2View(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER())))
1129 // Append a field to the end of the header text.
1130 uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
1131 xField->setPropertyValue(UNO_NAME_NAME, uno::Any(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()));
1132 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
1133 xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, /*bAbsorb=*/false);
1137 SfxWatermarkItem aWatermarkItem;
1138 aWatermarkItem.SetText(aWatermark);
1139 SetWatermark(aWatermarkItem);
1142 if (bFooterIsNeeded)
1144 // If the footer is off, turn it on.
1145 bool bFooterIsOn = false;
1146 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
1147 if (!bFooterIsOn)
1148 xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::Any(true));
1150 // If the footer already contains a document header field, no need to do anything.
1151 uno::Reference<text::XText> xFooterText;
1152 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
1153 static OUString sFooter = SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCFOOTER();
1154 if (!lcl_hasField(xFooterText, DocInfoServiceName, sFooter))
1156 // Append a field to the end of the footer text.
1157 uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
1158 xField->setPropertyValue(UNO_NAME_NAME, uno::Any(sFooter));
1159 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
1160 xFooterText->insertTextContent(xFooterText->getEnd(), xTextContent, /*bAbsorb=*/false);
1166 // We pass xParent and xNodeSubject even though they point to the same thing because the UNO_QUERY is
1167 // on a performance-sensitive path.
1168 static void lcl_ApplyParagraphClassification(SwDoc* pDoc,
1169 const uno::Reference<frame::XModel>& xModel,
1170 const rtl::Reference<SwXParagraph>& xParent,
1171 const css::uno::Reference<css::rdf::XResource>& xNodeSubject,
1172 std::vector<svx::ClassificationResult> aResults)
1174 if (!xNodeSubject.is())
1175 return;
1177 // Remove all paragraph classification fields.
1178 for (;;)
1180 uno::Reference<text::XTextField> xTextField = lcl_FindParagraphClassificationField(xModel, xParent);
1181 if (!xTextField.is())
1182 break;
1183 lcl_RemoveParagraphMetadataField(xTextField);
1186 if (aResults.empty())
1187 return;
1189 // Since we always insert at the start of the paragraph,
1190 // need to insert in reverse order.
1191 std::reverse(aResults.begin(), aResults.end());
1192 // Ignore "PARAGRAPH" types
1193 aResults.erase(std::remove_if(aResults.begin(),
1194 aResults.end(),
1195 [](const svx::ClassificationResult& rResult)-> bool
1196 { return rResult.meType == svx::ClassificationType::PARAGRAPH; }),
1197 aResults.end());
1199 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1200 std::vector<OUString> aFieldNames;
1201 for (size_t nIndex = 0; nIndex < aResults.size(); ++nIndex)
1203 const svx::ClassificationResult& rResult = aResults[nIndex];
1205 const bool isLast = nIndex == 0;
1206 const bool isFirst = (nIndex == aResults.size() - 1);
1207 OUString sKey;
1208 OUString sValue = rResult.msName;
1209 switch (rResult.meType)
1211 case svx::ClassificationType::TEXT:
1213 sKey = aKeyCreator.makeNumberedTextKey();
1215 break;
1217 case svx::ClassificationType::CATEGORY:
1219 if (rResult.msIdentifier.isEmpty())
1221 sKey = aKeyCreator.makeCategoryNameKey();
1223 else
1225 sValue = rResult.msIdentifier;
1226 sKey = aKeyCreator.makeCategoryIdentifierKey();
1228 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationAbbrRDFName, rResult.msAbbreviatedName);
1230 break;
1232 case svx::ClassificationType::MARKING:
1234 sKey = aKeyCreator.makeNumberedMarkingKey();
1236 break;
1238 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
1240 sKey = aKeyCreator.makeNumberedIntellectualPropertyPartKey();
1242 break;
1244 default:
1245 break;
1248 OUString sDisplayText = (isFirst ? ("(" + rResult.msAbbreviatedName) : rResult.msAbbreviatedName);
1249 if (isLast)
1250 sDisplayText += ")";
1251 lcl_UpdateParagraphClassificationField(pDoc, xModel, xParent, sKey, sValue, sDisplayText);
1252 aFieldNames.emplace_back(sKey);
1255 // Correct the order
1256 std::reverse(aFieldNames.begin(), aFieldNames.end());
1257 OUStringBuffer sFieldNames;
1258 bool first = true;
1259 for (const OUString& rFieldName : aFieldNames)
1261 if (!first)
1262 sFieldNames.append("/");
1263 sFieldNames.append(rFieldName);
1264 first = false;
1267 const OUString sOldFieldNames = lcl_getRDF(xModel, xNodeSubject, ParagraphClassificationFieldNamesRDFName).second;
1268 SwRDFHelper::removeStatement(xModel, MetaNS, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sOldFieldNames);
1269 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sFieldNames.makeStringAndClear());
1272 void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationResult> aResults)
1274 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1275 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1276 return;
1278 SwTextNode* pNode = GetCursor()->Start()->GetNode().GetTextNode();
1279 if (pNode == nullptr)
1280 return;
1282 // Prevent recursive validation since this is triggered on node updates, which we do below.
1283 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1284 comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
1285 SetParagraphSignatureValidation(bOldValidationFlag);
1288 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1289 rtl::Reference<SwXParagraph> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1290 lcl_ApplyParagraphClassification(GetDoc(), xModel, xParent, css::uno::Reference<css::rdf::XResource>(xParent), std::move(aResults));
1293 static std::vector<svx::ClassificationResult> lcl_CollectParagraphClassification(const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
1295 std::vector<svx::ClassificationResult> aResult;
1297 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
1298 if (!xTextPortionEnumerationAccess.is())
1299 return aResult;
1301 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
1303 const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1305 while (xTextPortions->hasMoreElements())
1307 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
1308 OUString aTextPortionType;
1309 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
1310 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
1311 continue;
1313 uno::Reference<lang::XServiceInfo> xField;
1314 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xField;
1315 if (!xField->supportsService(MetadataFieldServiceName))
1316 continue;
1318 uno::Reference<text::XTextField> xTextField(xField, uno::UNO_QUERY);
1319 const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
1320 const std::pair<OUString, OUString> rdfNamePair = lcl_getFieldRDFByPrefix(xModel, xTextField, sPolicy);
1322 uno::Reference<text::XTextRange> xTextRange(xField, uno::UNO_QUERY);
1323 const OUString aName = rdfNamePair.first;
1324 const OUString aValue = rdfNamePair.second;
1325 static const OUStringLiteral sBlank(u"");
1326 if (aKeyCreator.isMarkingTextKey(aName))
1328 aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
1330 else if (aKeyCreator.isCategoryNameKey(aName))
1332 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1334 else if (aKeyCreator.isCategoryIdentifierKey(aName))
1336 aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
1338 else if (aKeyCreator.isMarkingKey(aName))
1340 aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
1342 else if (aKeyCreator.isIntellectualPropertyPartKey(aName))
1344 aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, xTextRange->getString(), sBlank, sBlank });
1348 return aResult;
1351 std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
1353 std::vector<svx::ClassificationResult> aResult;
1355 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1356 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1357 return aResult;
1359 SwTextNode* pNode = GetCursor()->Start()->GetNode().GetTextNode();
1360 if (pNode == nullptr)
1361 return aResult;
1363 uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1364 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1365 return lcl_CollectParagraphClassification(xModel, xParent);
1368 static sal_Int16 lcl_GetAngle(const drawing::HomogenMatrix3& rMatrix)
1370 basegfx::B2DHomMatrix aTransformation;
1371 basegfx::B2DTuple aScale;
1372 basegfx::B2DTuple aTranslate;
1373 double fRotate = 0;
1374 double fShear = 0;
1376 aTransformation.set(0, 0, rMatrix.Line1.Column1);
1377 aTransformation.set(0, 1, rMatrix.Line1.Column2);
1378 aTransformation.set(0, 2, rMatrix.Line1.Column3);
1379 aTransformation.set(1, 0, rMatrix.Line2.Column1);
1380 aTransformation.set(1, 1, rMatrix.Line2.Column2);
1381 aTransformation.set(1, 2, rMatrix.Line2.Column3);
1382 // For this to be a valid 2D transform matrix, the last row must be [0,0,1]
1383 assert( rMatrix.Line3.Column1 == 0 );
1384 assert( rMatrix.Line3.Column2 == 0 );
1385 assert( rMatrix.Line3.Column3 == 1 );
1387 aTransformation.decompose(aScale, aTranslate, fRotate, fShear);
1388 sal_Int16 nDeg = round(basegfx::rad2deg(fRotate));
1389 return nDeg < 0 ? round(nDeg) * -1 : round(360.0 - nDeg);
1392 SfxWatermarkItem SwEditShell::GetWatermark() const
1394 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1395 if (!pDocShell)
1396 return SfxWatermarkItem();
1398 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1399 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1400 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1401 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1402 std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
1403 for (const OUString& rPageStyleName : aUsedPageStyles)
1405 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1407 bool bHeaderIsOn = false;
1408 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1409 if (!bHeaderIsOn)
1410 return SfxWatermarkItem();
1412 uno::Reference<text::XText> xHeaderText;
1413 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1415 OUString sWatermark = "";
1416 bool bSuccess = false;
1417 uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, "com.sun.star.drawing.CustomShape", sWatermark, bSuccess);
1419 if (xWatermark.is())
1421 SfxWatermarkItem aItem;
1422 uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
1423 uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
1424 Color nColor;
1425 sal_Int16 nTransparency;
1426 OUString aFont;
1427 drawing::HomogenMatrix3 aMatrix;
1429 aItem.SetText(xTextRange->getString());
1431 if (xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont)
1432 aItem.SetFont(aFont);
1433 if (xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor)
1434 aItem.SetColor(nColor);
1435 if (xPropertySet->getPropertyValue("Transformation") >>= aMatrix)
1436 aItem.SetAngle(lcl_GetAngle(aMatrix));
1437 if (xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency)
1438 aItem.SetTransparency(nTransparency);
1440 return aItem;
1443 return SfxWatermarkItem();
1446 static void lcl_placeWatermarkInHeader(const SfxWatermarkItem& rWatermark,
1447 const uno::Reference<frame::XModel>& xModel,
1448 const uno::Reference<beans::XPropertySet>& xPageStyle,
1449 const uno::Reference<text::XText>& xHeaderText)
1451 if (!xHeaderText.is())
1452 return;
1454 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
1455 OUString aShapeServiceName = "com.sun.star.drawing.CustomShape";
1456 OUString sWatermark = WATERMARK_NAME;
1457 bool bSuccess = false;
1458 uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, sWatermark, bSuccess);
1460 bool bDeleteWatermark = rWatermark.GetText().isEmpty();
1461 if (xWatermark.is())
1463 drawing::HomogenMatrix3 aMatrix;
1464 Color nColor = 0xc0c0c0;
1465 sal_Int16 nTransparency = 50;
1466 sal_Int16 nAngle = 45;
1467 OUString aFont = "";
1469 uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
1470 xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont;
1471 xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor;
1472 xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency;
1473 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
1474 nAngle = lcl_GetAngle(aMatrix);
1476 // If the header already contains a watermark, see if it its text is up to date.
1477 uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
1478 if (xTextRange->getString() != rWatermark.GetText()
1479 || aFont != rWatermark.GetFont()
1480 || nColor != rWatermark.GetColor()
1481 || nAngle != rWatermark.GetAngle()
1482 || nTransparency != rWatermark.GetTransparency()
1483 || bDeleteWatermark)
1485 // No: delete it and we'll insert a replacement.
1486 uno::Reference<lang::XComponent> xComponent(xWatermark, uno::UNO_QUERY);
1487 xComponent->dispose();
1488 xWatermark.clear();
1492 if (!bSuccess || xWatermark.is() || bDeleteWatermark)
1493 return;
1495 const OUString& sFont = rWatermark.GetFont();
1496 sal_Int16 nAngle = rWatermark.GetAngle();
1497 sal_Int16 nTransparency = rWatermark.GetTransparency();
1498 Color nColor = rWatermark.GetColor();
1500 // Calc the ratio.
1501 double fRatio = 0;
1503 ScopedVclPtrInstance<VirtualDevice> pDevice;
1504 vcl::Font aFont = pDevice->GetFont();
1505 aFont.SetFamilyName(sFont);
1506 aFont.SetFontSize(Size(0, 96));
1507 pDevice->SetFont(aFont);
1509 auto nTextWidth = pDevice->GetTextWidth(rWatermark.GetText());
1510 if (nTextWidth)
1512 fRatio = pDevice->GetTextHeight();
1513 fRatio /= nTextWidth;
1516 // Calc the size.
1517 sal_Int32 nWidth = 0;
1518 awt::Size aSize;
1519 xPageStyle->getPropertyValue(UNO_NAME_SIZE) >>= aSize;
1520 if (aSize.Width < aSize.Height)
1522 // Portrait.
1523 sal_Int32 nLeftMargin = 0;
1524 xPageStyle->getPropertyValue(UNO_NAME_LEFT_MARGIN) >>= nLeftMargin;
1525 sal_Int32 nRightMargin = 0;
1526 xPageStyle->getPropertyValue(UNO_NAME_RIGHT_MARGIN) >>= nRightMargin;
1527 nWidth = aSize.Width - nLeftMargin - nRightMargin;
1529 else
1531 // Landscape.
1532 sal_Int32 nTopMargin = 0;
1533 xPageStyle->getPropertyValue(UNO_NAME_TOP_MARGIN) >>= nTopMargin;
1534 sal_Int32 nBottomMargin = 0;
1535 xPageStyle->getPropertyValue(UNO_NAME_BOTTOM_MARGIN) >>= nBottomMargin;
1536 nWidth = aSize.Height - nTopMargin - nBottomMargin;
1538 sal_Int32 nHeight = fRatio * nWidth;
1540 // Create and insert the shape.
1541 uno::Reference<drawing::XShape> xShape(xMultiServiceFactory->createInstance(aShapeServiceName), uno::UNO_QUERY);
1543 uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
1544 xNamed->setName(sWatermark);
1546 basegfx::B2DHomMatrix aTransformation;
1547 aTransformation.identity();
1548 aTransformation.scale(nWidth, nHeight);
1549 aTransformation.rotate(-basegfx::deg2rad(nAngle));
1550 drawing::HomogenMatrix3 aMatrix;
1551 aMatrix.Line1.Column1 = aTransformation.get(0, 0);
1552 aMatrix.Line1.Column2 = aTransformation.get(0, 1);
1553 aMatrix.Line1.Column3 = aTransformation.get(0, 2);
1554 aMatrix.Line2.Column1 = aTransformation.get(1, 0);
1555 aMatrix.Line2.Column2 = aTransformation.get(1, 1);
1556 aMatrix.Line2.Column3 = aTransformation.get(1, 2);
1557 aMatrix.Line3.Column1 = 0;
1558 aMatrix.Line3.Column2 = 0;
1559 aMatrix.Line3.Column3 = 1;
1560 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1561 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, uno::Any(text::TextContentAnchorType_AT_CHARACTER));
1562 uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
1563 xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, false);
1565 // The remaining properties have to be set after the shape is inserted: do that in one batch to avoid flickering.
1566 uno::Reference<document::XActionLockable> xLockable(xShape, uno::UNO_QUERY);
1567 xLockable->addActionLock();
1568 xPropertySet->setPropertyValue(UNO_NAME_FILLCOLOR, uno::Any(static_cast<sal_Int32>(nColor)));
1569 xPropertySet->setPropertyValue(UNO_NAME_FILLSTYLE, uno::Any(drawing::FillStyle_SOLID));
1570 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(nTransparency));
1571 xPropertySet->setPropertyValue(UNO_NAME_LINESTYLE, uno::Any(drawing::LineStyle_NONE));
1572 xPropertySet->setPropertyValue(UNO_NAME_OPAQUE, uno::Any(false));
1573 xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::Any(false));
1574 xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::Any(false));
1575 xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT, uno::Any(nHeight));
1576 xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH, uno::Any(nWidth));
1577 xPropertySet->setPropertyValue(UNO_NAME_TEXT_WRAP, uno::Any(text::WrapTextMode_THROUGH));
1578 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
1579 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
1580 xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME, uno::Any(sFont));
1581 xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME_ASIAN, uno::Any(sFont));
1582 xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME_COMPLEX, uno::Any(sFont));
1583 xPropertySet->setPropertyValue(UNO_NAME_CHAR_HEIGHT, uno::Any(WATERMARK_AUTO_SIZE));
1584 xPropertySet->setPropertyValue("Transformation", uno::Any(aMatrix));
1586 uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
1587 xTextRange->setString(rWatermark.GetText());
1589 uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
1590 xDefaulter->createCustomShapeDefaults("fontwork-plain-text");
1592 auto aGeomPropSeq = xPropertySet->getPropertyValue("CustomShapeGeometry").get< uno::Sequence<beans::PropertyValue> >();
1593 auto aGeomPropVec = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aGeomPropSeq);
1594 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1596 {"TextPath", uno::Any(true)},
1597 }));
1598 auto it = std::find_if(aGeomPropVec.begin(), aGeomPropVec.end(), [](const beans::PropertyValue& rValue)
1600 return rValue.Name == "TextPath";
1602 if (it == aGeomPropVec.end())
1603 aGeomPropVec.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues));
1604 else
1605 it->Value <<= aPropertyValues;
1606 xPropertySet->setPropertyValue("CustomShapeGeometry", uno::Any(comphelper::containerToSequence(aGeomPropVec)));
1608 // tdf#108494, tdf#109313 the header height was switched to height of a watermark
1609 // and shape was moved to the lower part of a page, force position update
1610 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
1611 xPropertySet->setPropertyValue("Transformation", uno::Any(aMatrix));
1613 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT, uno::Any(text::HoriOrientation::CENTER));
1614 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT, uno::Any(text::VertOrientation::CENTER));
1616 xLockable->removeActionLock();
1619 void SwEditShell::SetWatermark(const SfxWatermarkItem& rWatermark)
1621 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1622 if (!pDocShell)
1623 return;
1624 const bool bNoWatermark = rWatermark.GetText().isEmpty();
1626 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1627 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1628 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1629 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1630 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
1632 for (const OUString& rPageStyleName : aStyles)
1634 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1636 // If the header is off, turn it on.
1637 bool bHeaderIsOn = false;
1638 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1639 if (!bHeaderIsOn)
1641 if (bNoWatermark)
1642 continue; // the style doesn't have any watermark - no need to do anything
1644 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
1647 // backup header height
1648 bool bDynamicHeight = true;
1649 sal_Int32 nOldValue;
1650 xPageStyle->getPropertyValue(UNO_NAME_HEADER_HEIGHT) >>= nOldValue;
1651 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT) >>= bDynamicHeight;
1652 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(false));
1654 // If the header already contains a document header field, no need to do anything.
1655 uno::Reference<text::XText> xHeaderText;
1656 uno::Reference<text::XText> xHeaderTextFirst;
1657 uno::Reference<text::XText> xHeaderTextLeft;
1658 uno::Reference<text::XText> xHeaderTextRight;
1660 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1661 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderText);
1663 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_FIRST) >>= xHeaderTextFirst;
1664 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextFirst);
1666 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_LEFT) >>= xHeaderTextLeft;
1667 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextLeft);
1669 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_RIGHT) >>= xHeaderTextRight;
1670 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextRight);
1672 // tdf#108494 the header height was switched to height of a watermark
1673 // and shape was moved to the lower part of a page
1674 xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::Any(sal_Int32(11)));
1675 xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::Any(nOldValue));
1676 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(bDynamicHeight));
1680 SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc& rDoc,
1681 uno::Reference<text::XTextField> xField,
1682 uno::Reference<text::XTextContent> xParent,
1683 const bool bRemove)
1684 : SwUndo(SwUndoId::PARA_SIGN_ADD, &rDoc),
1685 m_rDoc(rDoc),
1686 m_xField(std::move(xField)),
1687 m_xParent(std::move(xParent)),
1688 m_bRemove(bRemove)
1690 // Save the metadata and field content to undo/redo.
1691 uno::Reference<frame::XModel> xModel = m_rDoc.GetDocShell()->GetBaseModel();
1692 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, m_xField);
1693 const auto it = aStatements.find(ParagraphSignatureIdRDFName);
1694 if (it != aStatements.end())
1695 m_signature = it->second;
1697 const auto it2 = aStatements.find(ParagraphSignatureUsageRDFName);
1698 if (it2 != aStatements.end())
1699 m_usage = it2->second;
1701 uno::Reference<css::text::XTextRange> xText(m_xField, uno::UNO_QUERY);
1702 m_display = xText->getString();
1705 void SwUndoParagraphSigning::UndoImpl(::sw::UndoRedoContext&)
1707 if (m_bRemove)
1708 Remove();
1709 else
1710 Insert();
1713 void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext&)
1715 if (m_bRemove)
1716 Insert();
1717 else
1718 Remove();
1721 void SwUndoParagraphSigning::RepeatImpl(::sw::RepeatContext&)
1725 void SwUndoParagraphSigning::Insert()
1727 // Disable undo to avoid introducing noise when we edit the metadata field.
1728 const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
1729 m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
1731 // Prevent validation since this will trigger a premature validation
1732 // upon inserting, but before setting the metadata.
1733 SwEditShell* pEditSh = m_rDoc.GetEditShell();
1734 const bool bOldValidationFlag = pEditSh && pEditSh->SetParagraphSignatureValidation(false);
1735 comphelper::ScopeGuard const g([&] () {
1736 if (pEditSh)
1737 pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
1738 m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
1741 m_xField = lcl_InsertParagraphSignature(m_rDoc.GetDocShell()->GetBaseModel(), m_xParent, m_signature, m_usage);
1742 lcl_DoUpdateParagraphSignatureField(m_rDoc, m_xField, m_display);
1745 void SwUndoParagraphSigning::Remove()
1747 // Disable undo to avoid introducing noise when we edit the metadata field.
1748 const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
1749 m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
1751 // Prevent validation since this will trigger a premature validation
1752 // upon removing.
1753 SwEditShell* pEditSh = m_rDoc.GetEditShell();
1754 const bool bOldValidationFlag = pEditSh && pEditSh->SetParagraphSignatureValidation(false);
1755 comphelper::ScopeGuard const g([&] () {
1756 if (pEditSh)
1757 pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
1758 m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
1761 lcl_RemoveParagraphMetadataField(m_xField);
1764 void SwEditShell::SignParagraph()
1766 SwDoc& rDoc = *GetDoc();
1767 SwDocShell* pDocShell = rDoc.GetDocShell();
1768 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1769 return;
1770 const SwPosition* pPosStart = GetCursor()->Start();
1771 if (!pPosStart)
1772 return;
1773 SwTextNode* pNode = pPosStart->GetNode().GetTextNode();
1774 if (!pNode)
1775 return;
1777 // Table text signing is not supported.
1778 if (pNode->FindTableNode() != nullptr)
1779 return;
1781 // 1. Get the text (without fields).
1782 const uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1783 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
1784 if (utf8Text.isEmpty())
1785 return;
1787 // 2. Get certificate.
1788 uno::Reference<security::XDocumentDigitalSignatures> xSigner(
1789 // here none of the version-dependent methods are called
1790 security::DocumentDigitalSignatures::createDefault(
1791 comphelper::getProcessComponentContext()));
1793 uno::Sequence<css::beans::PropertyValue> aProperties;
1794 uno::Reference<security::XCertificate> xCertificate = xSigner->chooseCertificateWithProps(aProperties);
1795 if (!xCertificate.is())
1796 return;
1798 // 3. Sign it.
1799 svl::crypto::Signing signing(xCertificate);
1800 signing.AddDataRange(utf8Text.getStr(), utf8Text.getLength());
1801 OStringBuffer sigBuf;
1802 if (!signing.Sign(sigBuf))
1803 return;
1805 const OUString signature = OStringToOUString(sigBuf, RTL_TEXTENCODING_UTF8, 0);
1807 auto it = std::find_if(std::as_const(aProperties).begin(), std::as_const(aProperties).end(), [](const beans::PropertyValue& rValue)
1809 return rValue.Name == "Usage";
1812 OUString aUsage;
1813 if (it != std::as_const(aProperties).end())
1814 it->Value >>= aUsage;
1816 // 4. Add metadata
1817 // Prevent validation since this will trigger a premature validation
1818 // upon inserting, but before setting the metadata.
1819 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1820 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1821 SetParagraphSignatureValidation(bOldValidationFlag);
1824 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
1826 const uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1827 uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParagraph, signature, aUsage);
1829 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
1831 rDoc.GetIDocumentUndoRedo().AppendUndo(
1832 std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, true));
1834 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
1837 void SwEditShell::ValidateParagraphSignatures(SwTextNode* pNode, bool updateDontRemove)
1839 if (!pNode || !IsParagraphSignatureValidationEnabled())
1840 return;
1842 // Table text signing is not supported.
1843 if (pNode->FindTableNode() != nullptr)
1844 return;
1846 // Prevent recursive validation since this is triggered on node updates, which we do below.
1847 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1848 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1849 SetParagraphSignatureValidation(bOldValidationFlag);
1852 uno::Reference<text::XTextContent> xParentText = SwXParagraph::CreateXParagraph(*GetDoc(), pNode);
1853 lcl_ValidateParagraphSignatures(*GetDoc(), xParentText, updateDontRemove);
1856 void SwEditShell::ValidateCurrentParagraphSignatures(bool updateDontRemove)
1858 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1859 if (!pDocShell || !GetCursor() || !GetCursor()->Start() || !IsParagraphSignatureValidationEnabled())
1860 return;
1862 SwPaM* pPaM = GetCursor();
1863 const SwPosition* pPosStart = pPaM->Start();
1864 SwTextNode* pNode = pPosStart->GetNode().GetTextNode();
1865 ValidateParagraphSignatures(pNode, updateDontRemove);
1868 void SwEditShell::ValidateAllParagraphSignatures(bool updateDontRemove)
1870 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1871 if (!pDocShell || !IsParagraphSignatureValidationEnabled())
1872 return;
1874 // Prevent recursive validation since this is triggered on node updates, which we do below.
1875 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1876 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1877 SetParagraphSignatureValidation(bOldValidationFlag);
1880 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1881 const uno::Reference<text::XTextDocument> xDoc(xModel, uno::UNO_QUERY);
1882 uno::Reference<text::XText> xParent = xDoc->getText();
1883 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
1884 if (!xParagraphEnumerationAccess.is())
1885 return;
1886 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
1887 if (!xParagraphs.is())
1888 return;
1889 while (xParagraphs->hasMoreElements())
1891 uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
1892 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, updateDontRemove);
1896 static uno::Reference<text::XTextField> lcl_GetParagraphMetadataFieldAtIndex(const SwDocShell* pDocSh, SwTextNode const * pNode, const sal_uLong index)
1898 uno::Reference<text::XTextField> xTextField;
1899 if (pNode != nullptr && pDocSh != nullptr)
1901 SwTextAttr* pAttr = pNode->GetTextAttrAt(index, RES_TXTATR_METAFIELD);
1902 SwTextMeta* pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
1903 if (pTextMeta != nullptr)
1905 SwFormatMeta& rFormatMeta(static_cast<SwFormatMeta&>(pTextMeta->GetAttr()));
1906 if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
1908 const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
1909 uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
1910 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xSubject);
1911 if (aStatements.find(ParagraphSignatureIdRDFName) != aStatements.end() ||
1912 aStatements.find(ParagraphClassificationNameRDFName) != aStatements.end())
1914 xTextField = uno::Reference<text::XTextField>(xSubject, uno::UNO_QUERY);
1920 return xTextField;
1923 void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
1925 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1926 if (!pDocShell || !IsParagraphSignatureValidationEnabled())
1927 return;
1929 // Prevent recursive validation since this is triggered on node updates, which we do below.
1930 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1931 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1932 SetParagraphSignatureValidation(bOldValidationFlag);
1935 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1936 const rtl::Reference<SwXTextDocument> xDoc(dynamic_cast<SwXTextDocument*>(xModel.get()));
1937 rtl::Reference<SwXBodyText> xBodyText = xDoc->getBodyText();
1938 if (!xBodyText.is())
1939 return;
1940 rtl::Reference<SwXParagraphEnumeration> xParagraphs = xBodyText->createParagraphEnumeration();
1942 static constexpr OUStringLiteral sBlank(u"");
1943 const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1944 const css::uno::Sequence<css::uno::Reference<rdf::XURI>> aGraphNames = SwRDFHelper::getGraphNames(xModel, MetaNS);
1946 while (xParagraphs->hasMoreElements())
1948 uno::Reference<text::XTextContent> xParaOrTable(xParagraphs->nextElement(), uno::UNO_QUERY);
1949 rtl::Reference<SwXParagraph> xParagraph(dynamic_cast<SwXParagraph*>(xParaOrTable.get()));
1953 const css::uno::Reference<css::rdf::XResource> xSubject(xParagraph);
1954 const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, aGraphNames, xSubject);
1956 const auto it = aStatements.find(ParagraphClassificationFieldNamesRDFName);
1957 const OUString sFieldNames = (it != aStatements.end() ? it->second : sBlank);
1958 std::vector<svx::ClassificationResult> aResults;
1959 if (!sFieldNames.isEmpty())
1961 assert(it != aStatements.end() && "can only be non-empty if it was valid");
1962 // Order the fields
1963 sal_Int32 nIndex = 0;
1966 const OUString sCurFieldName = sFieldNames.getToken(0, '/', nIndex);
1967 if (sCurFieldName.isEmpty())
1968 break;
1970 const auto it2 = aStatements.find(sCurFieldName);
1971 bool bStatementFound = it2 != aStatements.end();
1972 const OUString sName = bStatementFound ? it->first : sBlank;
1973 const OUString sValue = bStatementFound ? it->second : sBlank;
1975 if (aKeyCreator.isMarkingTextKey(sName))
1977 aResults.push_back({ svx::ClassificationType::TEXT, sValue, sValue, sBlank });
1979 else if (aKeyCreator.isCategoryNameKey(sName))
1981 const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
1982 const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
1983 aResults.push_back({ svx::ClassificationType::CATEGORY, sValue, sAbbreviatedName, sBlank });
1985 else if (aKeyCreator.isCategoryIdentifierKey(sName))
1987 const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
1988 const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
1989 aResults.push_back({ svx::ClassificationType::CATEGORY, sBlank, sAbbreviatedName, sValue });
1991 else if (aKeyCreator.isMarkingKey(sName))
1993 aResults.push_back({ svx::ClassificationType::MARKING, sValue, sValue, sBlank });
1995 else if (aKeyCreator.isIntellectualPropertyPartKey(sName))
1997 aResults.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, sValue, sValue, sBlank });
2000 while (nIndex >= 0);
2003 // Update classification based on results.
2004 lcl_ApplyParagraphClassification(GetDoc(), xModel, xParagraph, xSubject, aResults);
2006 // Get Signatures
2007 std::map<OUString, SignatureDescr> aSignatures;
2008 for (const auto& pair : lcl_getRDFStatements(xModel, uno::Reference<css::text::XTextContent>(xParagraph)))
2010 const OUString& sName = pair.first;
2011 if (sName.startsWith(ParagraphSignatureRDFNamespace))
2013 const OUString sSuffix = sName.copy(ParagraphSignatureRDFNamespace.getLength());
2014 const sal_Int32 index = sSuffix.indexOf(":");
2015 if (index >= 0)
2017 const OUString id = sSuffix.copy(0, index);
2018 const OUString type = sSuffix.copy(index);
2019 const OUString& sValue = pair.second;
2020 if (type == ParagraphSignatureDateRDFName)
2021 aSignatures[id].msDate = sValue;
2022 else if (type == ParagraphSignatureUsageRDFName)
2023 aSignatures[id].msUsage = sValue;
2024 else if (type == ParagraphSignatureDigestRDFName)
2025 aSignatures[id].msSignature = sValue;
2030 for (const auto& pair : aSignatures)
2032 uno::Reference<text::XTextField> xField = lcl_findFieldByRDF(xModel, xParagraph, ParagraphSignatureIdRDFName, pair.first);
2033 if (!xField.is())
2035 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
2036 xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
2038 // Add the signature at the end.
2039 xField->attach(xParagraph->getAnchor()->getEnd());
2041 const css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
2042 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphSignatureIdRDFName, pair.first);
2044 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
2045 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
2049 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, true); // Validate and Update signatures.
2051 catch (const std::exception&)
2057 bool SwEditShell::IsCursorInParagraphMetadataField() const
2059 if (GetCursor() && GetCursor()->Start())
2061 SwTextNode* pNode = GetCursor()->Start()->GetNode().GetTextNode();
2062 const sal_uLong index = GetCursor()->Start()->GetContentIndex();
2063 uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2064 return xField.is();
2067 return false;
2070 bool SwEditShell::RemoveParagraphMetadataFieldAtCursor()
2072 if (GetCursor() && GetCursor()->Start())
2074 SwTextNode* pNode = GetCursor()->Start()->GetNode().GetTextNode();
2075 sal_uLong index = GetCursor()->Start()->GetContentIndex();
2076 uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2077 if (!xField.is())
2079 // Try moving the cursor to see if we're _facing_ a metafield or not,
2080 // as opposed to being within one.
2081 index--; // Backspace moves left
2083 xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2086 if (xField.is())
2088 lcl_RemoveParagraphMetadataField(xField);
2089 return true;
2093 return false;
2096 static OUString lcl_GetParagraphClassification(SfxClassificationHelper & rHelper, sfx::ClassificationKeyCreator const & rKeyCreator,
2097 const uno::Reference<frame::XModel>& xModel, const rtl::Reference<SwXParagraph>& xParagraph)
2099 uno::Reference<text::XTextField> xTextField;
2100 xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryIdentifierKey());
2101 if (xTextField.is())
2103 const std::pair<OUString, OUString> rdfValuePair = lcl_getRDF(xModel, xTextField, ParagraphClassificationValueRDFName);
2104 return rHelper.GetBACNameForIdentifier(rdfValuePair.second);
2107 xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryNameKey());
2108 if (xTextField.is())
2110 return lcl_getRDF(xModel, xTextField, ParagraphClassificationNameRDFName).second;
2113 return OUString();
2116 static OUString lcl_GetHighestClassificationParagraphClass(SwPaM* pCursor)
2118 OUString sHighestClass;
2120 SwTextNode* pNode = pCursor->Start()->GetNode().GetTextNode();
2121 if (pNode == nullptr)
2122 return sHighestClass;
2124 SwDocShell* pDocShell = pNode->GetDoc().GetDocShell();
2125 if (!pDocShell)
2126 return sHighestClass;
2128 SfxClassificationHelper aHelper(pDocShell->getDocProperties());
2129 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
2131 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
2132 const rtl::Reference<SwXTextDocument> xDoc(dynamic_cast<SwXTextDocument*>(xModel.get()));
2133 rtl::Reference<SwXBodyText> xBodyText = xDoc->getBodyText();
2135 rtl::Reference<SwXParagraphEnumeration> xParagraphs = xBodyText->createParagraphEnumeration();
2136 while (xParagraphs->hasMoreElements())
2138 uno::Reference<text::XTextContent> xParaOrTable(xParagraphs->nextElement(), uno::UNO_QUERY);
2139 rtl::Reference<SwXParagraph> xParagraph(dynamic_cast<SwXParagraph*>(xParaOrTable.get()));
2140 const OUString sCurrentClass = lcl_GetParagraphClassification(aHelper, aKeyCreator, xModel, xParagraph);
2141 sHighestClass = aHelper.GetHigherClass(sHighestClass, sCurrentClass);
2144 return sHighestClass;
2147 void SwEditShell::ClassifyDocPerHighestParagraphClass()
2149 SwDocShell* pDocShell = GetDoc()->GetDocShell();
2150 if (!pDocShell)
2151 return;
2153 // Bail out as early as possible if we don't have paragraph classification.
2154 if (!SwRDFHelper::hasMetadataGraph(pDocShell->GetBaseModel(), MetaNS))
2155 return;
2157 uno::Reference<document::XDocumentProperties> xDocumentProperties = pDocShell->getDocProperties();
2158 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
2160 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
2161 SfxClassificationHelper aHelper(xDocumentProperties);
2163 OUString sHighestClass = lcl_GetHighestClassificationParagraphClass(GetCursor());
2165 const OUString aClassificationCategory = svx::classification::getProperty(xPropertyContainer, aKeyCreator.makeCategoryNameKey());
2167 if (!aClassificationCategory.isEmpty())
2169 sHighestClass = aHelper.GetHigherClass(sHighestClass, aClassificationCategory);
2172 if (aClassificationCategory != sHighestClass)
2174 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
2175 VclMessageType::Question, VclButtonsType::Ok,
2176 SwResId(STR_CLASSIFICATION_LEVEL_CHANGED)));
2177 xQueryBox->run();
2180 const SfxClassificationPolicyType eHighestClassType = SfxClassificationHelper::stringToPolicyType(sHighestClass);
2182 // Prevent paragraph signature validation since the below changes (f.e. watermarking) are benign.
2183 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
2184 comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
2185 SetParagraphSignatureValidation(bOldValidationFlag);
2188 // Check the origin, if "manual" (created via advanced classification dialog),
2189 // then we just need to set the category name.
2190 if (sfx::getCreationOriginProperty(xPropertyContainer, aKeyCreator) == sfx::ClassificationCreationOrigin::MANUAL)
2192 aHelper.SetBACName(sHighestClass, eHighestClassType);
2193 ApplyAdvancedClassification(CollectAdvancedClassification());
2195 else
2197 SetClassification(sHighestClass, eHighestClassType);
2201 // #i62675#
2202 void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
2203 const bool bResetListAttrs)
2205 SwTextFormatColl *pLocal = pFormat? pFormat: (*GetDoc()->GetTextFormatColls())[0];
2206 StartAllAction();
2208 SwRewriter aRewriter;
2210 aRewriter.AddRule(UndoArg1, pLocal->GetName());
2212 GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter);
2213 for(SwPaM& rPaM : GetCursor()->GetRingContainer())
2215 if (!rPaM.HasReadonlySel( GetViewOptions()->IsFormView(), true))
2217 // store previous paragraph style for track changes
2218 OUString sParaStyleName;
2219 sal_uInt16 nPoolId = USHRT_MAX;
2220 SwContentNode * pCnt = rPaM.Start()->GetNode().GetContentNode();
2221 if ( pCnt && pCnt->GetTextNode() && GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() )
2223 const SwTextFormatColl* pTextFormatColl = pCnt->GetTextNode()->GetTextColl();
2224 sal_uInt16 nStylePoolId = pTextFormatColl->GetPoolFormatId();
2225 // default paragraph style
2226 if ( nStylePoolId == RES_POOLCOLL_STANDARD )
2227 nPoolId = nStylePoolId;
2228 else
2229 sParaStyleName = pTextFormatColl->GetName();
2232 // Change the paragraph style to pLocal and remove all direct paragraph formatting.
2233 GetDoc()->SetTextFormatColl(rPaM, pLocal, true, bResetListAttrs, GetLayout());
2235 // If there are hints on the nodes which cover the whole node, then remove those, too.
2236 SwPaM aPaM(*rPaM.Start(), *rPaM.End());
2237 if (SwTextNode* pEndTextNode = aPaM.End()->GetNode().GetTextNode())
2239 aPaM.Start()->SetContent(0);
2240 aPaM.End()->SetContent(pEndTextNode->GetText().getLength());
2242 GetDoc()->RstTextAttrs(aPaM, /*bInclRefToxMark=*/false, /*bExactRange=*/true, GetLayout());
2244 // add redline tracking the previous paragraph style
2245 if ( GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() &&
2246 // multi-paragraph ParagraphFormat redline ranges
2247 // haven't supported by AppendRedline(), yet
2248 // TODO handle multi-paragraph selections, too,
2249 // e.g. by breaking them to single paragraphs
2250 aPaM.Start()->GetNode() == aPaM.End()->GetNode() )
2252 SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::ParagraphFormat, aPaM );
2253 auto const result(GetDoc()->getIDocumentRedlineAccess().AppendRedline( pRedline, true));
2254 // store original paragraph style to reject formatting change
2255 if ( IDocumentRedlineAccess::AppendResult::IGNORED != result &&
2256 ( nPoolId == RES_POOLCOLL_STANDARD || !sParaStyleName.isEmpty() ) )
2258 std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
2259 xExtra.reset(new SwRedlineExtraData_FormatColl(sParaStyleName, nPoolId, nullptr));
2260 if (xExtra)
2261 pRedline->SetExtraData( xExtra.get() );
2267 GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL, &aRewriter);
2268 EndAllAction();
2271 SwTextFormatColl* SwEditShell::MakeTextFormatColl(const OUString& rFormatCollName,
2272 SwTextFormatColl* pParent)
2274 SwTextFormatColl *pColl;
2275 if ( pParent == nullptr )
2276 pParent = &GetTextFormatColl(0);
2277 pColl = GetDoc()->MakeTextFormatColl(rFormatCollName, pParent);
2278 if ( pColl == nullptr )
2280 OSL_FAIL( "MakeTextFormatColl failed" );
2282 return pColl;
2286 void SwEditShell::FillByEx(SwTextFormatColl* pColl)
2288 SwPaM * pCursor = GetCursor();
2289 SwContentNode * pCnt = pCursor->GetPointContentNode();
2290 if (pCnt->IsTextNode()) // uhm... what nonsense would happen if not?
2291 { // only need properties-node because BREAK/PAGEDESC filtered anyway!
2292 pCnt = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->GetNode());
2294 const SfxItemSet* pSet = pCnt->GetpSwAttrSet();
2295 if( !pSet )
2296 return;
2298 // JP 05.10.98: Special treatment if one of the attributes Break/PageDesc/NumRule(auto) is
2299 // in the ItemSet. Otherwise there will be too much or wrong processing (NumRules!)
2300 // Bug 57568
2302 // Do NOT copy AutoNumRules into the template
2303 const SwNumRuleItem* pItem;
2304 const SwNumRule* pRule = nullptr;
2305 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false)
2306 || SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false)
2307 || ((pItem = pSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
2308 && nullptr != (pRule = GetDoc()->FindNumRulePtr(pItem->GetValue()))
2309 && pRule->IsAutoRule()))
2311 SfxItemSet aSet( *pSet );
2312 aSet.ClearItem( RES_BREAK );
2313 aSet.ClearItem( RES_PAGEDESC );
2315 if (pRule
2316 || ((pItem = pSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
2317 && nullptr != (pRule = GetDoc()->FindNumRulePtr(pItem->GetValue()))
2318 && pRule->IsAutoRule()))
2319 aSet.ClearItem( RES_PARATR_NUMRULE );
2321 if( aSet.Count() )
2322 GetDoc()->ChgFormat(*pColl, aSet );
2324 else
2325 GetDoc()->ChgFormat(*pColl, *pSet );
2328 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */