nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / core / edit / edfcol.cxx
blob1dfcbf7f2d66c1753c6f949228eb252875e00e8d
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 <hintids.hxx>
72 #include <doc.hxx>
73 #include <IDocumentUndoRedo.hxx>
74 #include <ndtxt.hxx>
75 #include <paratr.hxx>
76 #include <viewopt.hxx>
77 #include <SwRewriter.hxx>
78 #include <numrule.hxx>
79 #include <swundo.hxx>
80 #include <docary.hxx>
81 #include <docsh.hxx>
82 #include <unoprnms.hxx>
83 #include <rootfrm.hxx>
84 #include <pagefrm.hxx>
85 #include <txtfrm.hxx>
86 #include <rdfhelper.hxx>
87 #include <sfx2/watermarkitem.hxx>
89 #include <unoparagraph.hxx>
90 #include <strings.hrc>
91 #include <undobj.hxx>
92 #include <UndoParagraphSignature.hxx>
93 #include <txtatr.hxx>
94 #include <fmtmeta.hxx>
96 #include <tools/diagnose_ex.h>
97 #include <IDocumentRedlineAccess.hxx>
98 #include <SwStyleNameMapper.hxx>
99 #include <comphelper/lok.hxx>
101 #define WATERMARK_NAME "PowerPlusWaterMarkObject"
102 #define WATERMARK_AUTO_SIZE sal_uInt32(1)
104 namespace
106 constexpr OUStringLiteral MetaFilename(u"tscp/bails.rdf");
107 constexpr OUStringLiteral MetaNS(u"urn:bails");
108 constexpr OUStringLiteral ParagraphSignatureRDFNamespace = u"urn:bails:loext:paragraph:signature:";
109 constexpr OUStringLiteral ParagraphSignatureIdRDFName = u"urn:bails:loext:paragraph:signature:id";
110 constexpr OUStringLiteral ParagraphSignatureDigestRDFName = u":digest";
111 constexpr OUStringLiteral ParagraphSignatureDateRDFName = u":date";
112 constexpr OUStringLiteral ParagraphSignatureUsageRDFName = u":usage";
113 constexpr OUStringLiteral ParagraphSignatureLastIdRDFName = u"urn:bails:loext:paragraph:signature:lastid";
114 constexpr OUStringLiteral ParagraphClassificationNameRDFName = u"urn:bails:loext:paragraph:classification:name";
115 constexpr OUStringLiteral ParagraphClassificationValueRDFName = u"urn:bails:loext:paragraph:classification:value";
116 constexpr OUStringLiteral ParagraphClassificationAbbrRDFName = u"urn:bails:loext:paragraph:classification:abbreviation";
117 constexpr OUStringLiteral ParagraphClassificationFieldNamesRDFName = u"urn:bails:loext:paragraph:classification:fields";
118 constexpr OUStringLiteral MetadataFieldServiceName = u"com.sun.star.text.textfield.MetadataField";
119 constexpr OUStringLiteral DocInfoServiceName = u"com.sun.star.text.TextField.DocInfo.Custom";
121 /// Find all page styles which are currently used in the document.
122 std::vector<OUString> lcl_getUsedPageStyles(SwViewShell const * pShell)
124 std::vector<OUString> aReturn;
126 SwRootFrame* pLayout = pShell->GetLayout();
127 for (SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
129 SwPageFrame* pPage = static_cast<SwPageFrame*>(pFrame);
130 if (const SwPageDesc *pDesc = pPage->FindPageDesc())
131 aReturn.push_back(pDesc->GetName());
134 return aReturn;
137 /// Search for a field named rFieldName of type rServiceName in xText and return it.
138 uno::Reference<text::XTextField> lcl_findField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, const OUString& rFieldName)
140 uno::Reference<text::XTextField> xField;
141 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
142 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
143 while (xParagraphs->hasMoreElements())
145 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
146 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
147 while (xTextPortions->hasMoreElements())
149 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
150 OUString aTextPortionType;
151 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
152 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
153 continue;
155 uno::Reference<lang::XServiceInfo> xTextField;
156 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
157 if (!xTextField->supportsService(rServiceName))
158 continue;
160 OUString aName;
161 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
162 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
163 if (aName == rFieldName)
165 xField = uno::Reference<text::XTextField>(xTextField, uno::UNO_QUERY);
166 break;
171 return xField;
174 /// Search for a field named rFieldName of type rServiceName in xText and return true iff found.
175 bool lcl_hasField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, const OUString& rFieldName)
177 return lcl_findField(xText, rServiceName, rFieldName).is();
180 /// Search for a frame with WATERMARK_NAME in name of type rServiceName in xText. Returns found name in rShapeName.
181 uno::Reference<drawing::XShape> lcl_getWatermark(const uno::Reference<text::XText>& xText,
182 const OUString& rServiceName, OUString& rShapeName, bool& bSuccess)
184 bSuccess = false;
185 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
186 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
187 while (xParagraphs->hasMoreElements())
189 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
190 if (!xTextPortionEnumerationAccess.is())
191 continue;
193 bSuccess = true;
195 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
196 while (xTextPortions->hasMoreElements())
198 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
199 OUString aTextPortionType;
200 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
201 if (aTextPortionType != "Frame")
202 continue;
204 uno::Reference<container::XContentEnumerationAccess> xContentEnumerationAccess(xTextPortion, uno::UNO_QUERY);
205 if (!xContentEnumerationAccess.is())
206 continue;
208 uno::Reference<container::XEnumeration> xEnumeration = xContentEnumerationAccess->createContentEnumeration("com.sun.star.text.TextContent");
209 if (!xEnumeration->hasMoreElements())
210 continue;
212 uno::Reference<lang::XServiceInfo> xWatermark(xEnumeration->nextElement(), uno::UNO_QUERY);
213 if (!xWatermark->supportsService(rServiceName))
214 continue;
216 uno::Reference<container::XNamed> xNamed(xWatermark, uno::UNO_QUERY);
218 if (!xNamed->getName().match(WATERMARK_NAME))
219 continue;
221 rShapeName = xNamed->getName();
223 uno::Reference<drawing::XShape> xShape(xWatermark, uno::UNO_QUERY);
224 return xShape;
228 return uno::Reference<drawing::XShape>();
231 /// Extract the text of the paragraph without any of the fields.
232 /// TODO: Consider moving to SwTextNode, or extend ModelToViewHelper.
233 OString lcl_getParagraphBodyText(const uno::Reference<text::XTextContent>& xText)
235 OUStringBuffer strBuf;
236 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xText, uno::UNO_QUERY);
237 if (!xTextPortionEnumerationAccess.is())
238 return OString();
240 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
241 while (xTextPortions->hasMoreElements())
243 uno::Any elem = xTextPortions->nextElement();
245 //TODO: Consider including hidden and conditional texts/portions.
246 OUString aTextPortionType;
247 uno::Reference<beans::XPropertySet> xPropertySet(elem, uno::UNO_QUERY);
248 xPropertySet->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
249 if (aTextPortionType == "Text")
251 uno::Reference<text::XTextRange> xTextRange(elem, uno::UNO_QUERY);
252 if (xTextRange.is())
253 strBuf.append(xTextRange->getString());
257 // Cleanup the dummy characters added by fields (which we exclude).
258 comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDSTART);
259 comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDEND);
260 comphelper::string::remove(strBuf, CH_TXTATR_BREAKWORD);
262 return strBuf.makeStringAndClear().trim().toUtf8();
265 template <typename T>
266 std::map<OUString, OUString> lcl_getRDFStatements(const uno::Reference<frame::XModel>& xModel,
267 const T& xRef)
271 const css::uno::Reference<css::rdf::XResource> xSubject(xRef, uno::UNO_QUERY);
272 return SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
274 catch (const ::css::uno::Exception&)
278 return std::map<OUString, OUString>();
281 /// Returns RDF (key, value) pair associated with the field, if any.
282 std::pair<OUString, OUString> lcl_getFieldRDFByPrefix(const uno::Reference<frame::XModel>& xModel,
283 const uno::Reference<css::text::XTextField>& xField,
284 std::u16string_view sPrefix)
286 for (const auto& pair : lcl_getRDFStatements(xModel, xField))
288 if (pair.first.startsWith(sPrefix))
289 return pair;
292 return std::make_pair(OUString(), OUString());
295 /// Returns RDF (key, value) pair associated with the field, if any.
296 template <typename T>
297 std::pair<OUString, OUString> lcl_getRDF(const uno::Reference<frame::XModel>& xModel,
298 const T& xRef,
299 const OUString& sRDFName)
301 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xRef);
302 const auto it = aStatements.find(sRDFName);
303 return (it != aStatements.end()) ? std::make_pair(it->first, it->second) : std::make_pair(OUString(), OUString());
306 /// Returns true iff the field in question is paragraph signature.
307 /// Note: must have associated RDF, since signatures are otherwise just metadata fields.
308 bool lcl_IsParagraphSignatureField(const uno::Reference<frame::XModel>& xModel,
309 const uno::Reference<css::text::XTextField>& xField)
311 return (lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).first == ParagraphSignatureIdRDFName);
314 uno::Reference<text::XTextField> lcl_findFieldByRDF(const uno::Reference<frame::XModel>& xModel,
315 const uno::Reference<text::XTextContent>& xParagraph,
316 const OUString& sRDFName,
317 const OUString& sRDFValue)
319 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
320 if (!xTextPortionEnumerationAccess.is())
321 return uno::Reference<text::XTextField>();
323 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
324 if (!xTextPortions.is())
325 return uno::Reference<text::XTextField>();
327 while (xTextPortions->hasMoreElements())
329 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
330 OUString aTextPortionType;
331 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
332 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
333 continue;
335 uno::Reference<lang::XServiceInfo> xTextField;
336 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
337 if (!xTextField->supportsService(MetadataFieldServiceName))
338 continue;
340 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
341 const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xField, sRDFName);
342 if (pair.first == sRDFName && (sRDFValue.isEmpty() || sRDFValue == pair.second))
343 return xField;
346 return uno::Reference<text::XTextField>();
349 struct SignatureDescr
351 OUString msSignature;
352 OUString msUsage;
353 OUString msDate;
355 bool isValid() const { return !msSignature.isEmpty(); }
358 SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
359 const uno::Reference<css::text::XTextContent>& xParagraph,
360 const OUString& sFieldId)
362 SignatureDescr aDescr;
364 const OUString prefix = ParagraphSignatureRDFNamespace + sFieldId;
365 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xParagraph);
367 const auto itSig = aStatements.find(prefix + ParagraphSignatureDigestRDFName);
368 aDescr.msSignature = (itSig != aStatements.end() ? itSig->second : OUString());
370 const auto itDate = aStatements.find(prefix + ParagraphSignatureDateRDFName);
371 aDescr.msDate = (itDate != aStatements.end() ? itDate->second : OUString());
373 const auto itUsage = aStatements.find(prefix + ParagraphSignatureUsageRDFName);
374 aDescr.msUsage = (itUsage != aStatements.end() ? itUsage->second : OUString());
376 return aDescr;
379 SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
380 const uno::Reference<css::text::XTextContent>& xParagraph,
381 const uno::Reference<css::text::XTextField>& xField)
383 const OUString sFieldId = lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).second;
384 if (!sFieldId.isEmpty())
385 return lcl_getSignatureDescr(xModel, xParagraph, sFieldId);
387 return SignatureDescr();
390 /// Validate and create the signature field display text from the fields.
391 std::pair<bool, OUString> lcl_MakeParagraphSignatureFieldText(const SignatureDescr& aDescr,
392 const OString& utf8Text)
394 OUString msg = SwResId(STR_INVALID_SIGNATURE);
395 bool valid = false;
397 if (aDescr.isValid())
399 const char* pData = utf8Text.getStr();
400 const std::vector<unsigned char> data(pData, pData + utf8Text.getLength());
402 OString encSignature;
403 if (aDescr.msSignature.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
405 const std::vector<unsigned char> sig(svl::crypto::DecodeHexString(encSignature));
406 SignatureInformation aInfo(0);
407 valid = svl::crypto::Signing::Verify(data, false, sig, aInfo);
408 valid = valid
409 && aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
411 assert(aInfo.GetSigningCertificate()); // it was valid
412 msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " +
413 aDescr.msDate;
414 msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": "));
415 msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID));
419 return std::make_pair(valid, msg);
422 /// Validate and return validation result and signature field display text.
423 std::pair<bool, OUString>
424 lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
425 const uno::Reference<css::text::XTextContent>& xParagraph,
426 const uno::Reference<css::text::XTextField>& xField,
427 const OString& utf8Text)
429 const SignatureDescr aDescr = lcl_getSignatureDescr(xModel, xParagraph, xField);
430 return lcl_MakeParagraphSignatureFieldText(aDescr, utf8Text);
433 /// Generate the next valid ID for the new signature on this paragraph.
434 OUString lcl_getNextSignatureId(const uno::Reference<frame::XModel>& xModel,
435 const uno::Reference<text::XTextContent>& xParagraph)
437 const OUString sFieldId = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName).second;
438 return OUString::number(!sFieldId.isEmpty() ? sFieldId.toInt32() + 1 : 1);
441 /// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
442 uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Reference<frame::XModel>& xModel,
443 const uno::Reference<text::XTextContent>& xParagraph,
444 const OUString& signature,
445 const OUString& usage)
447 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
448 auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
450 // Add the signature at the end.
451 xField->attach(xParagraph->getAnchor()->getEnd());
453 const OUString sId = lcl_getNextSignatureId(xModel, xParagraph);
455 const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
456 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureIdRDFName, sId);
458 // First convert the UTC UNIX timestamp to a tools::DateTime then to local time.
459 DateTime aDateTime = DateTime::CreateFromUnixTime(time(nullptr));
460 aDateTime.ConvertToLocalTime();
461 OUStringBuffer rBuffer;
462 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetYear()));
463 rBuffer.append('-');
464 if (aDateTime.GetMonth() < 10)
465 rBuffer.append('0');
466 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
467 rBuffer.append('-');
468 if (aDateTime.GetDay() < 10)
469 rBuffer.append('0');
470 rBuffer.append(static_cast<sal_Int32>(aDateTime.GetDay()));
472 // Now set the RDF on the paragraph, since that's what is preserved in .doc(x).
473 const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
474 const OUString prefix = ParagraphSignatureRDFNamespace + sId;
475 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureLastIdRDFName, sId);
476 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDigestRDFName, signature);
477 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureUsageRDFName, usage);
478 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDateRDFName, rBuffer.makeStringAndClear());
480 return xField;
483 /// Updates the signature field text if changed and returns true only iff updated.
484 bool lcl_DoUpdateParagraphSignatureField(SwDoc& rDoc,
485 const uno::Reference<css::text::XTextField>& xField,
486 const OUString& sDisplayText)
488 // Disable undo to avoid introducing noise when we edit the metadata field.
489 const bool isUndoEnabled = rDoc.GetIDocumentUndoRedo().DoesUndo();
490 rDoc.GetIDocumentUndoRedo().DoUndo(false);
491 comphelper::ScopeGuard const g([&rDoc, isUndoEnabled]() {
492 rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
497 uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
498 const OUString curText = xText->getString();
499 if (curText != sDisplayText)
501 xText->setString(sDisplayText);
502 return true;
505 catch (const uno::Exception&)
507 // We failed; avoid crashing.
508 DBG_UNHANDLED_EXCEPTION("sw.uno", "Failed to update paragraph signature");
511 return false;
514 /// Updates the signature field text if changed and returns true only iff updated.
515 bool lcl_UpdateParagraphSignatureField(SwDoc& rDoc,
516 const uno::Reference<frame::XModel>& xModel,
517 const uno::Reference<css::text::XTextContent>& xParagraph,
518 const uno::Reference<css::text::XTextField>& xField,
519 const OString& utf8Text)
521 const OUString sDisplayText
522 = lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).second;
523 return lcl_DoUpdateParagraphSignatureField(rDoc, xField, sDisplayText);
526 void lcl_RemoveParagraphMetadataField(const uno::Reference<css::text::XTextField>& xField)
528 uno::Reference<css::text::XTextRange> xParagraph(xField->getAnchor());
529 xParagraph->getText()->removeTextContent(xField);
532 /// Returns true iff the field in question is paragraph classification.
533 /// Note: must have associated RDF, since classifications are otherwise just metadata fields.
534 bool lcl_IsParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
535 const uno::Reference<css::text::XTextField>& xField,
536 const OUString& sKey)
538 const std::pair<OUString, OUString> rdfPair = lcl_getRDF(xModel, xField, ParagraphClassificationNameRDFName);
539 return rdfPair.first == ParagraphClassificationNameRDFName && (sKey.isEmpty() || rdfPair.second == sKey);
542 uno::Reference<text::XTextField> lcl_FindParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
543 const uno::Reference<text::XTextContent>& xParagraph,
544 const OUString& sKey = OUString())
546 uno::Reference<text::XTextField> xTextField;
548 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
549 if (!xTextPortionEnumerationAccess.is())
550 return xTextField;
552 // Enumerate text portions to find metadata fields. This is expensive, best to enumerate fields only.
553 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
554 while (xTextPortions->hasMoreElements())
556 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
557 OUString aTextPortionType;
558 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
559 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
560 continue;
562 uno::Reference<lang::XServiceInfo> xServiceInfo;
563 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xServiceInfo;
564 if (!xServiceInfo->supportsService(MetadataFieldServiceName))
565 continue;
567 uno::Reference<text::XTextField> xField(xServiceInfo, uno::UNO_QUERY);
568 if (lcl_IsParagraphClassificationField(xModel, xField, sKey))
570 xTextField = xField;
571 break;
575 return xTextField;
578 /// Creates and inserts Paragraph Classification Metadata field and creates the RDF entry
579 uno::Reference<text::XTextField> lcl_InsertParagraphClassification(const uno::Reference<frame::XModel>& xModel,
580 const uno::Reference<text::XTextContent>& xParent)
582 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
583 auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
585 // Add the classification at the start.
586 xField->attach(xParent->getAnchor()->getStart());
587 return xField;
590 /// Updates the paragraph classification field text if changed and returns true only iff updated.
591 bool lcl_UpdateParagraphClassificationField(SwDoc* pDoc,
592 const uno::Reference<frame::XModel>& xModel,
593 const uno::Reference<css::text::XTextContent>& xTextNode,
594 const OUString& sKey,
595 const OUString& sValue,
596 const OUString& sDisplayText)
598 // Disable undo to avoid introducing noise when we edit the metadata field.
599 const bool isUndoEnabled = pDoc->GetIDocumentUndoRedo().DoesUndo();
600 pDoc->GetIDocumentUndoRedo().DoUndo(false);
601 comphelper::ScopeGuard const g([pDoc, isUndoEnabled] () {
602 pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
605 uno::Reference<text::XTextField> xField = lcl_InsertParagraphClassification(xModel, xTextNode);
607 css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
608 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, sKey, sValue);
609 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationNameRDFName, sKey);
610 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationValueRDFName, sValue);
612 css::uno::Reference<css::rdf::XResource> xNodeSubject(xTextNode, uno::UNO_QUERY);
613 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, sKey, sValue);
615 return lcl_DoUpdateParagraphSignatureField(*pDoc, xField, sDisplayText);
618 void lcl_ValidateParagraphSignatures(SwDoc& rDoc, const uno::Reference<text::XTextContent>& xParagraph, const bool updateDontRemove)
620 SwDocShell* pDocShell = rDoc.GetDocShell();
621 if (!pDocShell)
622 return;
624 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
626 // Check if the paragraph is signed.
629 const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName);
630 if (pair.second.isEmpty())
631 return;
633 catch (const ::css::uno::Exception&)
635 return;
638 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
639 if (!xTextPortionEnumerationAccess.is())
640 return;
642 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
643 if (!xTextPortions.is())
644 return;
646 // Get the text (without fields).
647 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
648 if (utf8Text.isEmpty())
649 return;
651 while (xTextPortions->hasMoreElements())
653 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
654 OUString aTextPortionType;
655 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
656 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
657 continue;
659 uno::Reference<lang::XServiceInfo> xTextField;
660 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
661 if (!xTextField->supportsService(MetadataFieldServiceName))
662 continue;
664 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
665 if (!lcl_IsParagraphSignatureField(xModel, xField))
667 continue;
670 if (updateDontRemove)
672 lcl_UpdateParagraphSignatureField(rDoc, xModel, xParagraph, xField, utf8Text);
674 else if (!lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).first)
676 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
677 rDoc.GetIDocumentUndoRedo().AppendUndo(
678 std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, false));
679 lcl_RemoveParagraphMetadataField(xField);
680 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
685 } // anonymous namespace
687 SwTextFormatColl& SwEditShell::GetDfltTextFormatColl() const
689 return *GetDoc()->GetDfltTextFormatColl();
692 sal_uInt16 SwEditShell::GetTextFormatCollCount() const
694 return GetDoc()->GetTextFormatColls()->size();
697 SwTextFormatColl& SwEditShell::GetTextFormatColl(sal_uInt16 nFormatColl) const
699 return *((*(GetDoc()->GetTextFormatColls()))[nFormatColl]);
702 static void insertFieldToDocument(uno::Reference<lang::XMultiServiceFactory> const & rxMultiServiceFactory,
703 uno::Reference<text::XText> const & rxText, uno::Reference<text::XParagraphCursor> const & rxParagraphCursor,
704 OUString const & rsKey)
706 uno::Reference<beans::XPropertySet> xField(rxMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
707 xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(rsKey));
708 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
710 rxText->insertTextContent(rxParagraphCursor, xTextContent, false);
713 static void removeAllClassificationFields(std::u16string_view rPolicy, uno::Reference<text::XText> const & rxText)
715 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(rxText, uno::UNO_QUERY);
716 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
717 while (xParagraphs->hasMoreElements())
719 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
720 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
721 while (xTextPortions->hasMoreElements())
723 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
724 OUString aTextPortionType;
725 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
726 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
727 continue;
729 uno::Reference<lang::XServiceInfo> xTextField;
730 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
731 if (!xTextField->supportsService(DocInfoServiceName))
732 continue;
734 OUString aName;
735 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
736 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
737 if (aName.startsWith(rPolicy))
739 uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
740 rxText->removeTextContent(xField);
746 static sal_Int32 getNumberOfParagraphs(uno::Reference<text::XText> const & xText)
748 uno::Reference<container::XEnumerationAccess> xParagraphEnumAccess(xText, uno::UNO_QUERY);
749 uno::Reference<container::XEnumeration> xParagraphEnum = xParagraphEnumAccess->createEnumeration();
750 sal_Int32 nResult = 0;
751 while (xParagraphEnum->hasMoreElements())
753 xParagraphEnum->nextElement();
754 nResult++;
756 return nResult;
759 static void equaliseNumberOfParagraph(std::vector<svx::ClassificationResult> const & rResults, uno::Reference<text::XText> const & xText)
761 sal_Int32 nNumberOfParagraphs = 0;
762 for (svx::ClassificationResult const & rResult : rResults)
764 if (rResult.meType == svx::ClassificationType::PARAGRAPH)
765 nNumberOfParagraphs++;
768 while (getNumberOfParagraphs(xText) < nNumberOfParagraphs)
770 uno::Reference<text::XParagraphAppend> xParagraphAppend(xText, uno::UNO_QUERY);
771 xParagraphAppend->finishParagraph(uno::Sequence<beans::PropertyValue>());
775 void SwEditShell::ApplyAdvancedClassification(std::vector<svx::ClassificationResult> const & rResults)
777 SwDocShell* pDocShell = GetDoc()->GetDocShell();
778 if (!pDocShell || !SfxObjectShell::Current())
779 return;
781 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
782 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
783 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
784 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
786 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
788 uno::Reference<document::XDocumentProperties> xDocumentProperties = SfxObjectShell::Current()->getDocProperties();
790 const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
791 const std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
792 for (const OUString& rPageStyleName : aUsedPageStyles)
794 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
796 // HEADER
797 bool bHeaderIsOn = false;
798 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
799 uno::Reference<text::XText> xHeaderText;
800 if (bHeaderIsOn)
801 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
802 if (xHeaderText.is())
803 removeAllClassificationFields(sPolicy, xHeaderText);
805 // FOOTER
806 bool bFooterIsOn = false;
807 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
808 uno::Reference<text::XText> xFooterText;
809 if (bFooterIsOn)
810 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
811 if (xFooterText.is())
812 removeAllClassificationFields(sPolicy, xFooterText);
815 // Clear properties
816 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
817 svx::classification::removeAllProperties(xPropertyContainer);
819 SfxClassificationHelper aHelper(xDocumentProperties);
821 // Apply properties from the BA policy
822 for (svx::ClassificationResult const & rResult : rResults)
824 if (rResult.meType == svx::ClassificationType::CATEGORY)
826 aHelper.SetBACName(rResult.msName, SfxClassificationHelper::getPolicyType());
830 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
832 // Insert origin document property
833 svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::MANUAL);
835 // Insert full text as document property
836 svx::classification::insertFullTextualRepresentationAsDocumentProperty(xPropertyContainer, aCreator, rResults);
838 for (const OUString& rPageStyleName : aUsedPageStyles)
840 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
842 // HEADER
843 bool bHeaderIsOn = false;
844 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
845 if (!bHeaderIsOn)
846 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
847 uno::Reference<text::XText> xHeaderText;
848 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
849 equaliseNumberOfParagraph(rResults, xHeaderText);
851 // FOOTER
852 bool bFooterIsOn = false;
853 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
854 if (!bFooterIsOn)
855 xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::makeAny(true));
856 uno::Reference<text::XText> xFooterText;
857 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
858 equaliseNumberOfParagraph(rResults, xFooterText);
860 // SET/DELETE WATERMARK
861 SfxWatermarkItem aWatermarkItem;
862 aWatermarkItem.SetText(aHelper.GetDocumentWatermark());
863 SetWatermark(aWatermarkItem);
865 uno::Reference<text::XParagraphCursor> xHeaderParagraphCursor(xHeaderText->createTextCursor(), uno::UNO_QUERY);
866 uno::Reference<text::XParagraphCursor> xFooterParagraphCursor(xFooterText->createTextCursor(), uno::UNO_QUERY);
868 sal_Int32 nParagraph = -1;
870 for (svx::ClassificationResult const & rResult : rResults)
872 switch(rResult.meType)
874 case svx::ClassificationType::TEXT:
876 OUString sKey = aCreator.makeNumberedTextKey();
878 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
879 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
880 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
882 break;
884 case svx::ClassificationType::CATEGORY:
886 OUString sKey = aCreator.makeCategoryNameKey();
887 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
888 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
890 break;
892 case svx::ClassificationType::MARKING:
894 OUString sKey = aCreator.makeNumberedMarkingKey();
895 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
896 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
897 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
899 break;
901 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
903 OUString sKey = aCreator.makeNumberedIntellectualPropertyPartKey();
904 svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
905 insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
906 insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
908 break;
910 case svx::ClassificationType::PARAGRAPH:
912 nParagraph++;
914 if (nParagraph != 0) // only jump to next paragraph, if we aren't at the first paragraph
916 xHeaderParagraphCursor->gotoNextParagraph(false);
917 xFooterParagraphCursor->gotoNextParagraph(false);
920 xHeaderParagraphCursor->gotoStartOfParagraph(false);
921 xFooterParagraphCursor->gotoStartOfParagraph(false);
923 uno::Reference<beans::XPropertySet> xHeaderPropertySet(xHeaderParagraphCursor, uno::UNO_QUERY_THROW);
924 uno::Reference<beans::XPropertySet> xFooterPropertySet(xFooterParagraphCursor, uno::UNO_QUERY_THROW);
925 if (rResult.msName == "BOLD")
927 xHeaderPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::BOLD));
928 xFooterPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::BOLD));
930 else
932 xHeaderPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL));
933 xFooterPropertySet->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL));
936 break;
938 default:
939 break;
945 std::vector<svx::ClassificationResult> SwEditShell::CollectAdvancedClassification()
947 std::vector<svx::ClassificationResult> aResult;
949 SwDocShell* pDocShell = GetDoc()->GetDocShell();
950 if (!pDocShell || !SfxObjectShell::Current())
951 return aResult;
953 const OUString sBlank;
955 uno::Reference<document::XDocumentProperties> xDocumentProperties = SfxObjectShell::Current()->getDocProperties();
956 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
957 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
959 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
960 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
961 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
962 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
964 std::vector<OUString> aPageStyles = lcl_getUsedPageStyles(this);
965 OUString aPageStyleString = aPageStyles.back();
966 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(aPageStyleString), uno::UNO_QUERY);
968 bool bHeaderIsOn = false;
969 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
970 if (!bHeaderIsOn)
972 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
973 if (!aValue.isEmpty())
974 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
976 return aResult;
979 uno::Reference<text::XText> xHeaderText;
980 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
982 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xHeaderText, uno::UNO_QUERY);
983 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
985 // set to true if category was found in the header
986 bool bFoundClassificationCategory = false;
988 while (xParagraphs->hasMoreElements())
990 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
991 if (!xTextPortionEnumerationAccess.is())
992 continue;
993 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
995 // Check font weight
996 uno::Reference<beans::XPropertySet> xParagraphPropertySet(xTextPortionEnumerationAccess, uno::UNO_QUERY_THROW);
997 uno::Any aAny = xParagraphPropertySet->getPropertyValue("CharWeight");
999 OUString sWeight = (aAny.get<float>() >= awt::FontWeight::BOLD) ? OUString("BOLD") : OUString("NORMAL");
1001 aResult.push_back({ svx::ClassificationType::PARAGRAPH, sWeight, sBlank, sBlank });
1003 // Process portions
1004 while (xTextPortions->hasMoreElements())
1006 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
1007 OUString aTextPortionType;
1008 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
1009 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
1010 continue;
1012 uno::Reference<lang::XServiceInfo> xTextField;
1013 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
1014 if (!xTextField->supportsService(DocInfoServiceName))
1015 continue;
1017 OUString aName;
1018 uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
1019 xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
1021 if (aCreator.isMarkingTextKey(aName))
1023 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1024 if (!aValue.isEmpty())
1025 aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
1027 else if (aCreator.isCategoryNameKey(aName))
1029 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1030 if (!aValue.isEmpty())
1031 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1032 bFoundClassificationCategory = true;
1034 else if (aCreator.isCategoryIdentifierKey(aName))
1036 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1037 if (!aValue.isEmpty())
1038 aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
1039 bFoundClassificationCategory = true;
1041 else if (aCreator.isMarkingKey(aName))
1043 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1044 if (!aValue.isEmpty())
1045 aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
1047 else if (aCreator.isIntellectualPropertyPartKey(aName))
1049 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
1050 if (!aValue.isEmpty())
1051 aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, sBlank, sBlank });
1056 if (!bFoundClassificationCategory)
1058 const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
1059 if (!aValue.isEmpty())
1060 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1063 return aResult;
1066 void SwEditShell::SetClassification(const OUString& rName, SfxClassificationPolicyType eType)
1068 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1069 if (!pDocShell)
1070 return;
1072 SfxClassificationHelper aHelper(pDocShell->getDocProperties());
1074 const bool bHadWatermark = !aHelper.GetDocumentWatermark().isEmpty();
1076 // This updates the infobar as well.
1077 aHelper.SetBACName(rName, eType);
1079 // Insert origin document property
1080 uno::Reference<beans::XPropertyContainer> xPropertyContainer = pDocShell->getDocProperties()->getUserDefinedProperties();
1081 sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
1082 svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::BAF_POLICY);
1084 bool bHeaderIsNeeded = aHelper.HasDocumentHeader();
1085 bool bFooterIsNeeded = aHelper.HasDocumentFooter();
1086 OUString aWatermark = aHelper.GetDocumentWatermark();
1087 bool bWatermarkIsNeeded = !aWatermark.isEmpty();
1089 if (!bHeaderIsNeeded && !bFooterIsNeeded && !bWatermarkIsNeeded && !bHadWatermark)
1090 return;
1092 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1093 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1094 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1095 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1096 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
1098 for (const OUString& rPageStyleName : aStyles)
1100 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1101 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
1103 if (bHeaderIsNeeded || bWatermarkIsNeeded || bHadWatermark)
1105 // If the header is off, turn it on.
1106 bool bHeaderIsOn = false;
1107 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1108 if (!bHeaderIsOn)
1109 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
1111 // If the header already contains a document header field, no need to do anything.
1112 uno::Reference<text::XText> xHeaderText;
1113 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1115 if (bHeaderIsNeeded)
1117 if (!lcl_hasField(xHeaderText, DocInfoServiceName, SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()))
1119 // Append a field to the end of the header text.
1120 uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
1121 xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()));
1122 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
1123 xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, /*bAbsorb=*/false);
1127 SfxWatermarkItem aWatermarkItem;
1128 aWatermarkItem.SetText(aWatermark);
1129 SetWatermark(aWatermarkItem);
1132 if (bFooterIsNeeded)
1134 // If the footer is off, turn it on.
1135 bool bFooterIsOn = false;
1136 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
1137 if (!bFooterIsOn)
1138 xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::makeAny(true));
1140 // If the footer already contains a document header field, no need to do anything.
1141 uno::Reference<text::XText> xFooterText;
1142 xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
1143 static OUString sFooter = SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCFOOTER();
1144 if (!lcl_hasField(xFooterText, DocInfoServiceName, sFooter))
1146 // Append a field to the end of the footer text.
1147 uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
1148 xField->setPropertyValue(UNO_NAME_NAME, uno::makeAny(sFooter));
1149 uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
1150 xFooterText->insertTextContent(xFooterText->getEnd(), xTextContent, /*bAbsorb=*/false);
1156 // We pass xParent and xNodeSubject even though they point to the same thing because the UNO_QUERY is
1157 // on a performance-sensitive path.
1158 static void lcl_ApplyParagraphClassification(SwDoc* pDoc,
1159 const uno::Reference<frame::XModel>& xModel,
1160 const uno::Reference<text::XTextContent>& xParent,
1161 const css::uno::Reference<css::rdf::XResource>& xNodeSubject,
1162 std::vector<svx::ClassificationResult> aResults)
1164 if (!xNodeSubject.is())
1165 return;
1167 // Remove all paragraph classification fields.
1168 for (;;)
1170 uno::Reference<text::XTextField> xTextField = lcl_FindParagraphClassificationField(xModel, xParent);
1171 if (!xTextField.is())
1172 break;
1173 lcl_RemoveParagraphMetadataField(xTextField);
1176 if (aResults.empty())
1177 return;
1179 // Since we always insert at the start of the paragraph,
1180 // need to insert in reverse order.
1181 std::reverse(aResults.begin(), aResults.end());
1182 // Ignore "PARAGRAPH" types
1183 aResults.erase(std::remove_if(aResults.begin(),
1184 aResults.end(),
1185 [&](const svx::ClassificationResult& rResult)-> bool
1186 { return rResult.meType == svx::ClassificationType::PARAGRAPH; }),
1187 aResults.end());
1189 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1190 std::vector<OUString> aFieldNames;
1191 for (size_t nIndex = 0; nIndex < aResults.size(); ++nIndex)
1193 const svx::ClassificationResult& rResult = aResults[nIndex];
1195 const bool isLast = nIndex == 0;
1196 const bool isFirst = (nIndex == aResults.size() - 1);
1197 OUString sKey;
1198 OUString sValue = rResult.msName;
1199 switch (rResult.meType)
1201 case svx::ClassificationType::TEXT:
1203 sKey = aKeyCreator.makeNumberedTextKey();
1205 break;
1207 case svx::ClassificationType::CATEGORY:
1209 if (rResult.msIdentifier.isEmpty())
1211 sKey = aKeyCreator.makeCategoryNameKey();
1213 else
1215 sValue = rResult.msIdentifier;
1216 sKey = aKeyCreator.makeCategoryIdentifierKey();
1218 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationAbbrRDFName, rResult.msAbbreviatedName);
1220 break;
1222 case svx::ClassificationType::MARKING:
1224 sKey = aKeyCreator.makeNumberedMarkingKey();
1226 break;
1228 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
1230 sKey = aKeyCreator.makeNumberedIntellectualPropertyPartKey();
1232 break;
1234 default:
1235 break;
1238 OUString sDisplayText = (isFirst ? ("(" + rResult.msAbbreviatedName) : rResult.msAbbreviatedName);
1239 if (isLast)
1240 sDisplayText += ")";
1241 lcl_UpdateParagraphClassificationField(pDoc, xModel, xParent, sKey, sValue, sDisplayText);
1242 aFieldNames.emplace_back(sKey);
1245 // Correct the order
1246 std::reverse(aFieldNames.begin(), aFieldNames.end());
1247 OUStringBuffer sFieldNames;
1248 bool first = true;
1249 for (const OUString& rFieldName : aFieldNames)
1251 if (!first)
1252 sFieldNames.append("/");
1253 sFieldNames.append(rFieldName);
1254 first = false;
1257 const OUString sOldFieldNames = lcl_getRDF(xModel, xNodeSubject, ParagraphClassificationFieldNamesRDFName).second;
1258 SwRDFHelper::removeStatement(xModel, MetaNS, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sOldFieldNames);
1259 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sFieldNames.makeStringAndClear());
1262 void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationResult> aResults)
1264 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1265 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1266 return;
1268 SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
1269 if (pNode == nullptr)
1270 return;
1272 // Prevent recursive validation since this is triggered on node updates, which we do below.
1273 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1274 comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
1275 SetParagraphSignatureValidation(bOldValidationFlag);
1278 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1279 uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1280 lcl_ApplyParagraphClassification(GetDoc(), xModel, xParent, css::uno::Reference<css::rdf::XResource>(xParent, uno::UNO_QUERY), std::move(aResults));
1283 static std::vector<svx::ClassificationResult> lcl_CollectParagraphClassification(const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
1285 std::vector<svx::ClassificationResult> aResult;
1287 uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
1288 if (!xTextPortionEnumerationAccess.is())
1289 return aResult;
1291 uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
1293 const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1295 while (xTextPortions->hasMoreElements())
1297 uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
1298 OUString aTextPortionType;
1299 xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
1300 if (aTextPortionType != UNO_NAME_TEXT_FIELD)
1301 continue;
1303 uno::Reference<lang::XServiceInfo> xField;
1304 xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xField;
1305 if (!xField->supportsService(MetadataFieldServiceName))
1306 continue;
1308 uno::Reference<text::XTextField> xTextField(xField, uno::UNO_QUERY);
1309 const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
1310 const std::pair<OUString, OUString> rdfNamePair = lcl_getFieldRDFByPrefix(xModel, xTextField, sPolicy);
1312 uno::Reference<text::XTextRange> xTextRange(xField, uno::UNO_QUERY);
1313 const OUString aName = rdfNamePair.first;
1314 const OUString aValue = rdfNamePair.second;
1315 const OUString sBlank("");
1316 if (aKeyCreator.isMarkingTextKey(aName))
1318 aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
1320 else if (aKeyCreator.isCategoryNameKey(aName))
1322 aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
1324 else if (aKeyCreator.isCategoryIdentifierKey(aName))
1326 aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
1328 else if (aKeyCreator.isMarkingKey(aName))
1330 aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
1332 else if (aKeyCreator.isIntellectualPropertyPartKey(aName))
1334 aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, xTextRange->getString(), sBlank, sBlank });
1338 return aResult;
1341 std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
1343 std::vector<svx::ClassificationResult> aResult;
1345 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1346 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1347 return aResult;
1349 SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
1350 if (pNode == nullptr)
1351 return aResult;
1353 uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1354 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1355 return lcl_CollectParagraphClassification(xModel, xParent);
1358 static sal_Int16 lcl_GetAngle(const drawing::HomogenMatrix3& rMatrix)
1360 basegfx::B2DHomMatrix aTransformation;
1361 basegfx::B2DTuple aScale;
1362 basegfx::B2DTuple aTranslate;
1363 double fRotate = 0;
1364 double fShear = 0;
1366 aTransformation.set(0, 0, rMatrix.Line1.Column1);
1367 aTransformation.set(0, 1, rMatrix.Line1.Column2);
1368 aTransformation.set(0, 2, rMatrix.Line1.Column3);
1369 aTransformation.set(1, 0, rMatrix.Line2.Column1);
1370 aTransformation.set(1, 1, rMatrix.Line2.Column2);
1371 aTransformation.set(1, 2, rMatrix.Line2.Column3);
1372 aTransformation.set(2, 0, rMatrix.Line3.Column1);
1373 aTransformation.set(2, 1, rMatrix.Line3.Column2);
1374 aTransformation.set(2, 2, rMatrix.Line3.Column3);
1376 aTransformation.decompose(aScale, aTranslate, fRotate, fShear);
1377 sal_Int16 nDeg = round(basegfx::rad2deg(fRotate));
1378 return nDeg < 0 ? round(nDeg) * -1 : round(360.0 - nDeg);
1381 SfxWatermarkItem SwEditShell::GetWatermark() const
1383 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1384 if (!pDocShell)
1385 return SfxWatermarkItem();
1387 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1388 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1389 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1390 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1391 std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
1392 for (const OUString& rPageStyleName : aUsedPageStyles)
1394 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1396 bool bHeaderIsOn = false;
1397 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1398 if (!bHeaderIsOn)
1399 return SfxWatermarkItem();
1401 uno::Reference<text::XText> xHeaderText;
1402 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1404 OUString sWatermark = "";
1405 bool bSuccess = false;
1406 uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, "com.sun.star.drawing.CustomShape", sWatermark, bSuccess);
1408 if (xWatermark.is())
1410 SfxWatermarkItem aItem;
1411 uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
1412 uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
1413 Color nColor;
1414 sal_Int16 nTransparency;
1415 OUString aFont;
1416 drawing::HomogenMatrix3 aMatrix;
1418 aItem.SetText(xTextRange->getString());
1420 if (xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont)
1421 aItem.SetFont(aFont);
1422 if (xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor)
1423 aItem.SetColor(nColor);
1424 if (xPropertySet->getPropertyValue("Transformation") >>= aMatrix)
1425 aItem.SetAngle(lcl_GetAngle(aMatrix));
1426 if (xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency)
1427 aItem.SetTransparency(nTransparency);
1429 return aItem;
1432 return SfxWatermarkItem();
1435 static void lcl_placeWatermarkInHeader(const SfxWatermarkItem& rWatermark,
1436 const uno::Reference<frame::XModel>& xModel,
1437 const uno::Reference<beans::XPropertySet>& xPageStyle,
1438 const uno::Reference<text::XText>& xHeaderText)
1440 if (!xHeaderText.is())
1441 return;
1443 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
1444 OUString aShapeServiceName = "com.sun.star.drawing.CustomShape";
1445 OUString sWatermark = WATERMARK_NAME;
1446 bool bSuccess = false;
1447 uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, sWatermark, bSuccess);
1449 bool bDeleteWatermark = rWatermark.GetText().isEmpty();
1450 if (xWatermark.is())
1452 drawing::HomogenMatrix3 aMatrix;
1453 Color nColor = 0xc0c0c0;
1454 sal_Int16 nTransparency = 50;
1455 sal_Int16 nAngle = 45;
1456 OUString aFont = "";
1458 uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
1459 xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont;
1460 xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor;
1461 xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency;
1462 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
1463 nAngle = lcl_GetAngle(aMatrix);
1465 // If the header already contains a watermark, see if it its text is up to date.
1466 uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
1467 if (xTextRange->getString() != rWatermark.GetText()
1468 || aFont != rWatermark.GetFont()
1469 || nColor != rWatermark.GetColor()
1470 || nAngle != rWatermark.GetAngle()
1471 || nTransparency != rWatermark.GetTransparency()
1472 || bDeleteWatermark)
1474 // No: delete it and we'll insert a replacement.
1475 uno::Reference<lang::XComponent> xComponent(xWatermark, uno::UNO_QUERY);
1476 xComponent->dispose();
1477 xWatermark.clear();
1481 if (!bSuccess || xWatermark.is() || bDeleteWatermark)
1482 return;
1484 const OUString& sFont = rWatermark.GetFont();
1485 sal_Int16 nAngle = rWatermark.GetAngle();
1486 sal_Int16 nTransparency = rWatermark.GetTransparency();
1487 Color nColor = rWatermark.GetColor();
1489 // Calc the ratio.
1490 double fRatio = 0;
1492 ScopedVclPtrInstance<VirtualDevice> pDevice;
1493 vcl::Font aFont = pDevice->GetFont();
1494 aFont.SetFamilyName(sFont);
1495 aFont.SetFontSize(Size(0, 96));
1496 pDevice->SetFont(aFont);
1498 auto nTextWidth = pDevice->GetTextWidth(rWatermark.GetText());
1499 if (nTextWidth)
1501 fRatio = pDevice->GetTextHeight();
1502 fRatio /= nTextWidth;
1505 // Calc the size.
1506 sal_Int32 nWidth = 0;
1507 awt::Size aSize;
1508 xPageStyle->getPropertyValue(UNO_NAME_SIZE) >>= aSize;
1509 if (aSize.Width < aSize.Height)
1511 // Portrait.
1512 sal_Int32 nLeftMargin = 0;
1513 xPageStyle->getPropertyValue(UNO_NAME_LEFT_MARGIN) >>= nLeftMargin;
1514 sal_Int32 nRightMargin = 0;
1515 xPageStyle->getPropertyValue(UNO_NAME_RIGHT_MARGIN) >>= nRightMargin;
1516 nWidth = aSize.Width - nLeftMargin - nRightMargin;
1518 else
1520 // Landscape.
1521 sal_Int32 nTopMargin = 0;
1522 xPageStyle->getPropertyValue(UNO_NAME_TOP_MARGIN) >>= nTopMargin;
1523 sal_Int32 nBottomMargin = 0;
1524 xPageStyle->getPropertyValue(UNO_NAME_BOTTOM_MARGIN) >>= nBottomMargin;
1525 nWidth = aSize.Height - nTopMargin - nBottomMargin;
1527 sal_Int32 nHeight = fRatio * nWidth;
1529 // Create and insert the shape.
1530 uno::Reference<drawing::XShape> xShape(xMultiServiceFactory->createInstance(aShapeServiceName), uno::UNO_QUERY);
1532 uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
1533 xNamed->setName(sWatermark);
1535 basegfx::B2DHomMatrix aTransformation;
1536 aTransformation.identity();
1537 aTransformation.scale(nWidth, nHeight);
1538 aTransformation.rotate(-basegfx::deg2rad(nAngle));
1539 drawing::HomogenMatrix3 aMatrix;
1540 aMatrix.Line1.Column1 = aTransformation.get(0, 0);
1541 aMatrix.Line1.Column2 = aTransformation.get(0, 1);
1542 aMatrix.Line1.Column3 = aTransformation.get(0, 2);
1543 aMatrix.Line2.Column1 = aTransformation.get(1, 0);
1544 aMatrix.Line2.Column2 = aTransformation.get(1, 1);
1545 aMatrix.Line2.Column3 = aTransformation.get(1, 2);
1546 aMatrix.Line3.Column1 = aTransformation.get(2, 0);
1547 aMatrix.Line3.Column2 = aTransformation.get(2, 1);
1548 aMatrix.Line3.Column3 = aTransformation.get(2, 2);
1549 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1550 xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
1551 uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
1552 xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, false);
1554 // The remaining properties have to be set after the shape is inserted: do that in one batch to avoid flickering.
1555 uno::Reference<document::XActionLockable> xLockable(xShape, uno::UNO_QUERY);
1556 xLockable->addActionLock();
1557 xPropertySet->setPropertyValue(UNO_NAME_FILLCOLOR, uno::makeAny(static_cast<sal_Int32>(nColor)));
1558 xPropertySet->setPropertyValue(UNO_NAME_FILLSTYLE, uno::makeAny(drawing::FillStyle_SOLID));
1559 xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::makeAny(nTransparency));
1560 xPropertySet->setPropertyValue(UNO_NAME_LINESTYLE, uno::makeAny(drawing::LineStyle_NONE));
1561 xPropertySet->setPropertyValue(UNO_NAME_OPAQUE, uno::makeAny(false));
1562 xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::makeAny(false));
1563 xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::makeAny(false));
1564 xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT, uno::makeAny(nHeight));
1565 xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH, uno::makeAny(nWidth));
1566 xPropertySet->setPropertyValue(UNO_NAME_TEXT_WRAP, uno::makeAny(text::WrapTextMode_THROUGH));
1567 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, uno::makeAny(text::RelOrientation::PAGE_PRINT_AREA));
1568 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, uno::makeAny(text::RelOrientation::PAGE_PRINT_AREA));
1569 xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME, uno::makeAny(sFont));
1570 xPropertySet->setPropertyValue(UNO_NAME_CHAR_HEIGHT, uno::makeAny(WATERMARK_AUTO_SIZE));
1571 xPropertySet->setPropertyValue("Transformation", uno::makeAny(aMatrix));
1573 uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
1574 xTextRange->setString(rWatermark.GetText());
1576 uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
1577 xDefaulter->createCustomShapeDefaults("fontwork-plain-text");
1579 auto aGeomPropSeq = xPropertySet->getPropertyValue("CustomShapeGeometry").get< uno::Sequence<beans::PropertyValue> >();
1580 auto aGeomPropVec = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aGeomPropSeq);
1581 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
1583 {"TextPath", uno::makeAny(true)},
1584 }));
1585 auto it = std::find_if(aGeomPropVec.begin(), aGeomPropVec.end(), [](const beans::PropertyValue& rValue)
1587 return rValue.Name == "TextPath";
1589 if (it == aGeomPropVec.end())
1590 aGeomPropVec.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues));
1591 else
1592 it->Value <<= aPropertyValues;
1593 xPropertySet->setPropertyValue("CustomShapeGeometry", uno::makeAny(comphelper::containerToSequence(aGeomPropVec)));
1595 // tdf#108494, tdf#109313 the header height was switched to height of a watermark
1596 // and shape was moved to the lower part of a page, force position update
1597 xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
1598 xPropertySet->setPropertyValue("Transformation", uno::makeAny(aMatrix));
1600 xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT, uno::makeAny(text::HoriOrientation::CENTER));
1601 xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT, uno::makeAny(text::VertOrientation::CENTER));
1603 xLockable->removeActionLock();
1606 void SwEditShell::SetWatermark(const SfxWatermarkItem& rWatermark)
1608 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1609 if (!pDocShell)
1610 return;
1611 const bool bNoWatermark = rWatermark.GetText().isEmpty();
1613 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1614 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
1615 uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
1616 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
1617 const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
1619 for (const OUString& rPageStyleName : aStyles)
1621 uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
1623 // If the header is off, turn it on.
1624 bool bHeaderIsOn = false;
1625 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
1626 if (!bHeaderIsOn)
1628 if (bNoWatermark)
1629 continue; // the style doesn't have any watermark - no need to do anything
1631 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::makeAny(true));
1634 // backup header height
1635 bool bDynamicHeight = true;
1636 sal_Int32 nOldValue;
1637 xPageStyle->getPropertyValue(UNO_NAME_HEADER_HEIGHT) >>= nOldValue;
1638 xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT) >>= bDynamicHeight;
1639 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(false));
1641 // If the header already contains a document header field, no need to do anything.
1642 uno::Reference<text::XText> xHeaderText;
1643 uno::Reference<text::XText> xHeaderTextFirst;
1644 uno::Reference<text::XText> xHeaderTextLeft;
1645 uno::Reference<text::XText> xHeaderTextRight;
1647 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
1648 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderText);
1650 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_FIRST) >>= xHeaderTextFirst;
1651 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextFirst);
1653 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_LEFT) >>= xHeaderTextLeft;
1654 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextLeft);
1656 xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_RIGHT) >>= xHeaderTextRight;
1657 lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextRight);
1659 // tdf#108494 the header height was switched to height of a watermark
1660 // and shape was moved to the lower part of a page
1661 xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::makeAny(sal_Int32(11)));
1662 xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::makeAny(nOldValue));
1663 xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(bDynamicHeight));
1667 SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc& rDoc,
1668 const uno::Reference<text::XTextField>& xField,
1669 const uno::Reference<text::XTextContent>& xParent,
1670 const bool bRemove)
1671 : SwUndo(SwUndoId::PARA_SIGN_ADD, &rDoc),
1672 m_rDoc(rDoc),
1673 m_xField(xField),
1674 m_xParent(xParent),
1675 m_bRemove(bRemove)
1677 // Save the metadata and field content to undo/redo.
1678 uno::Reference<frame::XModel> xModel = m_rDoc.GetDocShell()->GetBaseModel();
1679 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, m_xField);
1680 const auto it = aStatements.find(ParagraphSignatureIdRDFName);
1681 if (it != aStatements.end())
1682 m_signature = it->second;
1684 const auto it2 = aStatements.find(ParagraphSignatureUsageRDFName);
1685 if (it2 != aStatements.end())
1686 m_usage = it2->second;
1688 uno::Reference<css::text::XTextRange> xText(m_xField, uno::UNO_QUERY);
1689 m_display = xText->getString();
1692 void SwUndoParagraphSigning::UndoImpl(::sw::UndoRedoContext&)
1694 if (m_bRemove)
1695 Remove();
1696 else
1697 Insert();
1700 void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext&)
1702 if (m_bRemove)
1703 Insert();
1704 else
1705 Remove();
1708 void SwUndoParagraphSigning::RepeatImpl(::sw::RepeatContext&)
1712 void SwUndoParagraphSigning::Insert()
1714 // Disable undo to avoid introducing noise when we edit the metadata field.
1715 const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
1716 m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
1718 // Prevent validation since this will trigger a premature validation
1719 // upon inserting, but before setting the metadata.
1720 SwEditShell* pEditSh = m_rDoc.GetEditShell();
1721 const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false);
1722 comphelper::ScopeGuard const g([&] () {
1723 pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
1724 m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
1727 m_xField = lcl_InsertParagraphSignature(m_rDoc.GetDocShell()->GetBaseModel(), m_xParent, m_signature, m_usage);
1728 lcl_DoUpdateParagraphSignatureField(m_rDoc, m_xField, m_display);
1731 void SwUndoParagraphSigning::Remove()
1733 // Disable undo to avoid introducing noise when we edit the metadata field.
1734 const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
1735 m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
1737 // Prevent validation since this will trigger a premature validation
1738 // upon removing.
1739 SwEditShell* pEditSh = m_rDoc.GetEditShell();
1740 const bool bOldValidationFlag = pEditSh->SetParagraphSignatureValidation(false);
1741 comphelper::ScopeGuard const g([&] () {
1742 pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
1743 m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
1746 lcl_RemoveParagraphMetadataField(m_xField);
1749 void SwEditShell::SignParagraph()
1751 SwDoc& rDoc = *GetDoc();
1752 SwDocShell* pDocShell = rDoc.GetDocShell();
1753 if (!pDocShell || !GetCursor() || !GetCursor()->Start())
1754 return;
1755 const SwPosition* pPosStart = GetCursor()->Start();
1756 if (!pPosStart)
1757 return;
1758 SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
1759 if (!pNode)
1760 return;
1762 // Table text signing is not supported.
1763 if (pNode->FindTableNode() != nullptr)
1764 return;
1766 // 1. Get the text (without fields).
1767 const uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
1768 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
1769 if (utf8Text.isEmpty())
1770 return;
1772 // 2. Get certificate.
1773 uno::Reference<security::XDocumentDigitalSignatures> xSigner(
1774 // here none of the version-dependent methods are called
1775 security::DocumentDigitalSignatures::createDefault(
1776 comphelper::getProcessComponentContext()));
1778 uno::Sequence<css::beans::PropertyValue> aProperties;
1779 uno::Reference<security::XCertificate> xCertificate = xSigner->chooseCertificateWithProps(aProperties);
1780 if (!xCertificate.is())
1781 return;
1783 // 3. Sign it.
1784 svl::crypto::Signing signing(xCertificate);
1785 signing.AddDataRange(utf8Text.getStr(), utf8Text.getLength());
1786 OStringBuffer sigBuf;
1787 if (!signing.Sign(sigBuf))
1788 return;
1790 const OUString signature = OStringToOUString(sigBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8, 0);
1792 std::vector<css::beans::PropertyValue> vec = comphelper::sequenceToContainer<std::vector<css::beans::PropertyValue>>(aProperties);
1793 auto it = std::find_if(vec.begin(), vec.end(), [](const beans::PropertyValue& rValue)
1795 return rValue.Name == "Usage";
1798 OUString aUsage;
1799 if (it != vec.end())
1800 it->Value >>= aUsage;
1802 // 4. Add metadata
1803 // Prevent validation since this will trigger a premature validation
1804 // upon inserting, but before setting the metadata.
1805 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1806 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1807 SetParagraphSignatureValidation(bOldValidationFlag);
1810 rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
1812 const uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1813 uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParagraph, signature, aUsage);
1815 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
1817 rDoc.GetIDocumentUndoRedo().AppendUndo(
1818 std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, true));
1820 rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
1823 void SwEditShell::ValidateParagraphSignatures(SwTextNode* pNode, bool updateDontRemove)
1825 if (!pNode || !IsParagraphSignatureValidationEnabled())
1826 return;
1828 // Table text signing is not supported.
1829 if (pNode->FindTableNode() != nullptr)
1830 return;
1832 // Prevent recursive validation since this is triggered on node updates, which we do below.
1833 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1834 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1835 SetParagraphSignatureValidation(bOldValidationFlag);
1838 uno::Reference<text::XTextContent> xParentText = SwXParagraph::CreateXParagraph(*GetDoc(), pNode);
1839 lcl_ValidateParagraphSignatures(*GetDoc(), xParentText, updateDontRemove);
1842 void SwEditShell::ValidateCurrentParagraphSignatures(bool updateDontRemove)
1844 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1845 if (!pDocShell || !GetCursor() || !GetCursor()->Start() || !IsParagraphSignatureValidationEnabled())
1846 return;
1848 SwPaM* pPaM = GetCursor();
1849 const SwPosition* pPosStart = pPaM->Start();
1850 SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
1851 ValidateParagraphSignatures(pNode, updateDontRemove);
1854 void SwEditShell::ValidateAllParagraphSignatures(bool updateDontRemove)
1856 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1857 if (!pDocShell || !IsParagraphSignatureValidationEnabled())
1858 return;
1860 // Prevent recursive validation since this is triggered on node updates, which we do below.
1861 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1862 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1863 SetParagraphSignatureValidation(bOldValidationFlag);
1866 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1867 const uno::Reference<text::XTextDocument> xDoc(xModel, uno::UNO_QUERY);
1868 uno::Reference<text::XText> xParent = xDoc->getText();
1869 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
1870 if (!xParagraphEnumerationAccess.is())
1871 return;
1872 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
1873 if (!xParagraphs.is())
1874 return;
1875 while (xParagraphs->hasMoreElements())
1877 uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
1878 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, updateDontRemove);
1882 static uno::Reference<text::XTextField> lcl_GetParagraphMetadataFieldAtIndex(const SwDocShell* pDocSh, SwTextNode const * pNode, const sal_uLong index)
1884 uno::Reference<text::XTextField> xTextField;
1885 if (pNode != nullptr && pDocSh != nullptr)
1887 SwTextAttr* pAttr = pNode->GetTextAttrAt(index, RES_TXTATR_METAFIELD);
1888 SwTextMeta* pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
1889 if (pTextMeta != nullptr)
1891 SwFormatMeta& rFormatMeta(static_cast<SwFormatMeta&>(pTextMeta->GetAttr()));
1892 if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
1894 const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
1895 uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
1896 const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xSubject);
1897 if (aStatements.find(ParagraphSignatureIdRDFName) != aStatements.end() ||
1898 aStatements.find(ParagraphClassificationNameRDFName) != aStatements.end())
1900 xTextField = uno::Reference<text::XTextField>(xSubject, uno::UNO_QUERY);
1906 return xTextField;
1909 void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
1911 SwDocShell* pDocShell = GetDoc()->GetDocShell();
1912 if (!pDocShell || !IsParagraphSignatureValidationEnabled())
1913 return;
1915 // Prevent recursive validation since this is triggered on node updates, which we do below.
1916 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
1917 comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
1918 SetParagraphSignatureValidation(bOldValidationFlag);
1921 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
1922 const uno::Reference<text::XTextDocument> xDoc(xModel, uno::UNO_QUERY);
1923 uno::Reference<text::XText> xParent = xDoc->getText();
1924 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
1925 if (!xParagraphEnumerationAccess.is())
1926 return;
1927 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
1928 if (!xParagraphs.is())
1929 return;
1931 static constexpr OUStringLiteral sBlank(u"");
1932 const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
1933 const css::uno::Sequence<css::uno::Reference<rdf::XURI>> aGraphNames = SwRDFHelper::getGraphNames(xModel, MetaNS);
1935 while (xParagraphs->hasMoreElements())
1937 uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
1941 const css::uno::Reference<css::rdf::XResource> xSubject(xParagraph, uno::UNO_QUERY);
1942 const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, aGraphNames, xSubject);
1944 const auto it = aStatements.find(ParagraphClassificationFieldNamesRDFName);
1945 const OUString sFieldNames = (it != aStatements.end() ? it->second : sBlank);
1946 std::vector<svx::ClassificationResult> aResults;
1947 if (!sFieldNames.isEmpty())
1949 // Order the fields
1950 sal_Int32 nIndex = 0;
1953 const OUString sCurFieldName = sFieldNames.getToken(0, '/', nIndex);
1954 if (sCurFieldName.isEmpty())
1955 break;
1957 const auto it2 = aStatements.find(sCurFieldName);
1958 const OUString sName = (it2 != aStatements.end() ? it->first : sBlank);
1959 const OUString sValue = (it2 != aStatements.end() ? it->second : sBlank);
1961 if (aKeyCreator.isMarkingTextKey(sName))
1963 aResults.push_back({ svx::ClassificationType::TEXT, sValue, sValue, sBlank });
1965 else if (aKeyCreator.isCategoryNameKey(sName))
1967 const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
1968 const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
1969 aResults.push_back({ svx::ClassificationType::CATEGORY, sValue, sAbbreviatedName, sBlank });
1971 else if (aKeyCreator.isCategoryIdentifierKey(sName))
1973 const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
1974 const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
1975 aResults.push_back({ svx::ClassificationType::CATEGORY, sBlank, sAbbreviatedName, sValue });
1977 else if (aKeyCreator.isMarkingKey(sName))
1979 aResults.push_back({ svx::ClassificationType::MARKING, sValue, sValue, sBlank });
1981 else if (aKeyCreator.isIntellectualPropertyPartKey(sName))
1983 aResults.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, sValue, sValue, sBlank });
1986 while (nIndex >= 0);
1989 // Update classification based on results.
1990 lcl_ApplyParagraphClassification(GetDoc(), xModel, xParagraph, xSubject, aResults);
1992 // Get Signatures
1993 std::map<OUString, SignatureDescr> aSignatures;
1994 for (const auto& pair : lcl_getRDFStatements(xModel, xParagraph))
1996 const OUString& sName = pair.first;
1997 if (sName.startsWith(ParagraphSignatureRDFNamespace))
1999 const OUString sSuffix = sName.copy(ParagraphSignatureRDFNamespace.getLength());
2000 const sal_Int32 index = sSuffix.indexOf(":");
2001 if (index >= 0)
2003 const OUString id = sSuffix.copy(0, index);
2004 const OUString type = sSuffix.copy(index);
2005 const OUString& sValue = pair.second;
2006 if (type == ParagraphSignatureDateRDFName)
2007 aSignatures[id].msDate = sValue;
2008 else if (type == ParagraphSignatureUsageRDFName)
2009 aSignatures[id].msUsage = sValue;
2010 else if (type == ParagraphSignatureDigestRDFName)
2011 aSignatures[id].msSignature = sValue;
2016 for (const auto& pair : aSignatures)
2018 uno::Reference<text::XTextField> xField = lcl_findFieldByRDF(xModel, xParagraph, ParagraphSignatureIdRDFName, pair.first);
2019 if (!xField.is())
2021 uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
2022 xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
2024 // Add the signature at the end.
2025 xField->attach(xParagraph->getAnchor()->getEnd());
2027 const css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
2028 SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphSignatureIdRDFName, pair.first);
2030 const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
2031 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
2035 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, true); // Validate and Update signatures.
2037 catch (const std::exception&)
2043 bool SwEditShell::IsCursorInParagraphMetadataField() const
2045 if (GetCursor() && GetCursor()->Start())
2047 SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
2048 const sal_uLong index = GetCursor()->Start()->nContent.GetIndex();
2049 uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2050 return xField.is();
2053 return false;
2056 bool SwEditShell::RemoveParagraphMetadataFieldAtCursor()
2058 if (GetCursor() && GetCursor()->Start())
2060 SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
2061 sal_uLong index = GetCursor()->Start()->nContent.GetIndex();
2062 uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2063 if (!xField.is())
2065 // Try moving the cursor to see if we're _facing_ a metafield or not,
2066 // as opposed to being within one.
2067 index--; // Backspace moves left
2069 xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
2072 if (xField.is())
2074 lcl_RemoveParagraphMetadataField(xField);
2075 return true;
2079 return false;
2082 static OUString lcl_GetParagraphClassification(SfxClassificationHelper & rHelper, sfx::ClassificationKeyCreator const & rKeyCreator,
2083 const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
2085 uno::Reference<text::XTextField> xTextField;
2086 xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryIdentifierKey());
2087 if (xTextField.is())
2089 const std::pair<OUString, OUString> rdfValuePair = lcl_getRDF(xModel, xTextField, ParagraphClassificationValueRDFName);
2090 return rHelper.GetBACNameForIdentifier(rdfValuePair.second);
2093 xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryNameKey());
2094 if (xTextField.is())
2096 return lcl_getRDF(xModel, xTextField, ParagraphClassificationNameRDFName).second;
2099 return OUString();
2102 static OUString lcl_GetHighestClassificationParagraphClass(SwPaM* pCursor)
2104 OUString sHighestClass;
2106 SwTextNode* pNode = pCursor->Start()->nNode.GetNode().GetTextNode();
2107 if (pNode == nullptr)
2108 return sHighestClass;
2110 SwDocShell* pDocShell = pNode->GetDoc().GetDocShell();
2111 if (!pDocShell)
2112 return sHighestClass;
2114 SfxClassificationHelper aHelper(pDocShell->getDocProperties());
2115 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
2117 uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
2118 const uno::Reference< text::XTextDocument > xDoc(xModel, uno::UNO_QUERY);
2119 uno::Reference<text::XText> xParent = xDoc->getText();
2121 uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
2122 uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
2123 while (xParagraphs->hasMoreElements())
2125 uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
2126 const OUString sCurrentClass = lcl_GetParagraphClassification(aHelper, aKeyCreator, xModel, xParagraph);
2127 sHighestClass = aHelper.GetHigherClass(sHighestClass, sCurrentClass);
2130 return sHighestClass;
2133 void SwEditShell::ClassifyDocPerHighestParagraphClass()
2135 SwDocShell* pDocShell = GetDoc()->GetDocShell();
2136 if (!pDocShell)
2137 return;
2139 // Bail out as early as possible if we don't have paragraph classification.
2140 if (!SwRDFHelper::hasMetadataGraph(pDocShell->GetBaseModel(), MetaNS))
2141 return;
2143 uno::Reference<document::XDocumentProperties> xDocumentProperties = pDocShell->getDocProperties();
2144 uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
2146 sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
2147 SfxClassificationHelper aHelper(xDocumentProperties);
2149 OUString sHighestClass = lcl_GetHighestClassificationParagraphClass(GetCursor());
2151 const OUString aClassificationCategory = svx::classification::getProperty(xPropertyContainer, aKeyCreator.makeCategoryNameKey());
2153 if (!aClassificationCategory.isEmpty())
2155 sHighestClass = aHelper.GetHigherClass(sHighestClass, aClassificationCategory);
2158 if (aClassificationCategory != sHighestClass)
2160 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
2161 VclMessageType::Question, VclButtonsType::Ok,
2162 SwResId(STR_CLASSIFICATION_LEVEL_CHANGED)));
2163 xQueryBox->run();
2166 const SfxClassificationPolicyType eHighestClassType = SfxClassificationHelper::stringToPolicyType(sHighestClass);
2168 // Prevent paragraph signature validation since the below changes (f.e. watermarking) are benign.
2169 const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
2170 comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
2171 SetParagraphSignatureValidation(bOldValidationFlag);
2174 // Check the origin, if "manual" (created via advanced classification dialog),
2175 // then we just need to set the category name.
2176 if (sfx::getCreationOriginProperty(xPropertyContainer, aKeyCreator) == sfx::ClassificationCreationOrigin::MANUAL)
2178 aHelper.SetBACName(sHighestClass, eHighestClassType);
2179 ApplyAdvancedClassification(CollectAdvancedClassification());
2181 else
2183 SetClassification(sHighestClass, eHighestClassType);
2187 // #i62675#
2188 void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
2189 const bool bResetListAttrs)
2191 SwTextFormatColl *pLocal = pFormat? pFormat: (*GetDoc()->GetTextFormatColls())[0];
2192 StartAllAction();
2194 RedlineFlags eRedlMode = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode;
2196 SwRewriter aRewriter;
2198 // in online we can have multiple languages, use universal name then
2199 if (comphelper::LibreOfficeKit::isActive())
2201 OUString aName;
2202 sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(pLocal->GetName(), SwGetPoolIdFromName::TxtColl);
2203 SwStyleNameMapper::FillProgName(nId, aName);
2204 if (!aName.isEmpty())
2205 pLocal->SetName(aName);
2208 aRewriter.AddRule(UndoArg1, pLocal->GetName());
2210 GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter);
2211 for(SwPaM& rPaM : GetCursor()->GetRingContainer())
2214 if ( !rPaM.HasReadonlySel( GetViewOptions()->IsFormView() ) )
2216 // tdf#105413 turn off ShowChanges mode for the next loops to apply styles permanently with redlining,
2217 // ie. in all directly preceding deleted paragraphs at the actual cursor positions
2218 if ( IDocumentRedlineAccess::IsShowChanges(eRedlMode) &&
2219 // is there redlining at beginning of the position (possible redline block before the modified node)
2220 GetDoc()->getIDocumentRedlineAccess().GetRedlinePos( (*rPaM.Start()).nNode.GetNode(), RedlineType::Any ) <
2221 GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size() )
2223 eRedlMode = RedlineFlags::ShowInsert | RedlineFlags::Ignore;
2224 GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode );
2227 // Change the paragraph style to pLocal and remove all direct paragraph formatting.
2228 GetDoc()->SetTextFormatColl(rPaM, pLocal, true, bResetListAttrs, GetLayout());
2230 // If there are hints on the nodes which cover the whole node, then remove those, too.
2231 SwPaM aPaM(*rPaM.Start(), *rPaM.End());
2232 if (SwTextNode* pEndTextNode = aPaM.End()->nNode.GetNode().GetTextNode())
2234 aPaM.Start()->nContent = 0;
2235 aPaM.End()->nContent = pEndTextNode->GetText().getLength();
2237 GetDoc()->RstTextAttrs(aPaM, /*bInclRefToxMark=*/false, /*bExactRange=*/true, GetLayout());
2241 GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL, &aRewriter);
2242 EndAllAction();
2244 GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode );
2247 SwTextFormatColl* SwEditShell::MakeTextFormatColl(const OUString& rFormatCollName,
2248 SwTextFormatColl* pParent)
2250 SwTextFormatColl *pColl;
2251 if ( pParent == nullptr )
2252 pParent = &GetTextFormatColl(0);
2253 pColl = GetDoc()->MakeTextFormatColl(rFormatCollName, pParent);
2254 if ( pColl == nullptr )
2256 OSL_FAIL( "MakeTextFormatColl failed" );
2258 return pColl;
2262 void SwEditShell::FillByEx(SwTextFormatColl* pColl)
2264 SwPaM * pCursor = GetCursor();
2265 SwContentNode * pCnt = pCursor->GetContentNode();
2266 if (pCnt->IsTextNode()) // uhm... what nonsense would happen if not?
2267 { // only need properties-node because BREAK/PAGEDESC filtered anyway!
2268 pCnt = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
2270 const SfxItemSet* pSet = pCnt->GetpSwAttrSet();
2271 if( !pSet )
2272 return;
2274 // JP 05.10.98: Special treatment if one of the attributes Break/PageDesc/NumRule(auto) is
2275 // in the ItemSet. Otherwise there will be too much or wrong processing (NumRules!)
2276 // Bug 57568
2278 // Do NOT copy AutoNumRules into the template
2279 const SfxPoolItem* pItem;
2280 const SwNumRule* pRule = nullptr;
2281 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false)
2282 || SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false)
2283 || (SfxItemState::SET == pSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem)
2284 && nullptr != (pRule = GetDoc()->FindNumRulePtr(
2285 static_cast<const SwNumRuleItem*>(pItem)->GetValue()))
2286 && pRule->IsAutoRule()))
2288 SfxItemSet aSet( *pSet );
2289 aSet.ClearItem( RES_BREAK );
2290 aSet.ClearItem( RES_PAGEDESC );
2292 if (pRule
2293 || (SfxItemState::SET == pSet->GetItemState(RES_PARATR_NUMRULE, false, &pItem)
2294 && nullptr != (pRule = GetDoc()->FindNumRulePtr(
2295 static_cast<const SwNumRuleItem*>(pItem)->GetValue()))
2296 && pRule->IsAutoRule()))
2297 aSet.ClearItem( RES_PARATR_NUMRULE );
2299 if( aSet.Count() )
2300 GetDoc()->ChgFormat(*pColl, aSet );
2302 else
2303 GetDoc()->ChgFormat(*pColl, *pSet );
2306 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */