1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
67 #include <vcl/svapp.hxx>
68 #include <vcl/weld.hxx>
69 #include <vcl/virdev.hxx>
71 #include <hintids.hxx>
73 #include <IDocumentUndoRedo.hxx>
76 #include <viewopt.hxx>
77 #include <SwRewriter.hxx>
78 #include <numrule.hxx>
82 #include <unoprnms.hxx>
83 #include <rootfrm.hxx>
84 #include <pagefrm.hxx>
86 #include <rdfhelper.hxx>
87 #include <sfx2/watermarkitem.hxx>
89 #include <unoparagraph.hxx>
90 #include <strings.hrc>
92 #include <UndoParagraphSignature.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)
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());
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
)
155 uno::Reference
<lang::XServiceInfo
> xTextField
;
156 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
157 if (!xTextField
->supportsService(rServiceName
))
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
);
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
)
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())
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")
204 uno::Reference
<container::XContentEnumerationAccess
> xContentEnumerationAccess(xTextPortion
, uno::UNO_QUERY
);
205 if (!xContentEnumerationAccess
.is())
208 uno::Reference
<container::XEnumeration
> xEnumeration
= xContentEnumerationAccess
->createContentEnumeration("com.sun.star.text.TextContent");
209 if (!xEnumeration
->hasMoreElements())
212 uno::Reference
<lang::XServiceInfo
> xWatermark(xEnumeration
->nextElement(), uno::UNO_QUERY
);
213 if (!xWatermark
->supportsService(rServiceName
))
216 uno::Reference
<container::XNamed
> xNamed(xWatermark
, uno::UNO_QUERY
);
218 if (!xNamed
->getName().match(WATERMARK_NAME
))
221 rShapeName
= xNamed
->getName();
223 uno::Reference
<drawing::XShape
> xShape(xWatermark
, uno::UNO_QUERY
);
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())
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
);
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
,
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
))
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
,
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
)
335 uno::Reference
<lang::XServiceInfo
> xTextField
;
336 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
337 if (!xTextField
->supportsService(MetadataFieldServiceName
))
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
))
346 return uno::Reference
<text::XTextField
>();
349 struct SignatureDescr
351 OUString msSignature
;
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());
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
);
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
);
409 && aInfo
.nStatus
== xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
411 assert(aInfo
.GetSigningCertificate()); // it was valid
412 msg
= SwResId(STR_SIGNED_BY
) + ": " + aInfo
.GetSigningCertificate()->X509Subject
+ ", " +
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()));
464 if (aDateTime
.GetMonth() < 10)
466 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
468 if (aDateTime
.GetDay() < 10)
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());
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
);
505 catch (const uno::Exception
&)
507 // We failed; avoid crashing.
508 DBG_UNHANDLED_EXCEPTION("sw.uno", "Failed to update paragraph signature");
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())
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
)
562 uno::Reference
<lang::XServiceInfo
> xServiceInfo
;
563 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xServiceInfo
;
564 if (!xServiceInfo
->supportsService(MetadataFieldServiceName
))
567 uno::Reference
<text::XTextField
> xField(xServiceInfo
, uno::UNO_QUERY
);
568 if (lcl_IsParagraphClassificationField(xModel
, xField
, sKey
))
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());
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();
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())
633 catch (const ::css::uno::Exception
&)
638 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraph
, uno::UNO_QUERY
);
639 if (!xTextPortionEnumerationAccess
.is())
642 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
643 if (!xTextPortions
.is())
646 // Get the text (without fields).
647 const OString utf8Text
= lcl_getParagraphBodyText(xParagraph
);
648 if (utf8Text
.isEmpty())
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
)
659 uno::Reference
<lang::XServiceInfo
> xTextField
;
660 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
661 if (!xTextField
->supportsService(MetadataFieldServiceName
))
664 uno::Reference
<text::XTextField
> xField(xTextField
, uno::UNO_QUERY
);
665 if (!lcl_IsParagraphSignatureField(xModel
, xField
))
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
)
729 uno::Reference
<lang::XServiceInfo
> xTextField
;
730 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
731 if (!xTextField
->supportsService(DocInfoServiceName
))
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();
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())
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
);
797 bool bHeaderIsOn
= false;
798 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
799 uno::Reference
<text::XText
> xHeaderText
;
801 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
802 if (xHeaderText
.is())
803 removeAllClassificationFields(sPolicy
, xHeaderText
);
806 bool bFooterIsOn
= false;
807 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_IS_ON
) >>= bFooterIsOn
;
808 uno::Reference
<text::XText
> xFooterText
;
810 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_TEXT
) >>= xFooterText
;
811 if (xFooterText
.is())
812 removeAllClassificationFields(sPolicy
, xFooterText
);
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
);
843 bool bHeaderIsOn
= false;
844 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= 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
);
852 bool bFooterIsOn
= false;
853 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_IS_ON
) >>= 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
);
884 case svx::ClassificationType::CATEGORY
:
886 OUString sKey
= aCreator
.makeCategoryNameKey();
887 insertFieldToDocument(xMultiServiceFactory
, xHeaderText
, xHeaderParagraphCursor
, sKey
);
888 insertFieldToDocument(xMultiServiceFactory
, xFooterText
, xFooterParagraphCursor
, sKey
);
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
);
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
);
910 case svx::ClassificationType::PARAGRAPH
:
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
));
932 xHeaderPropertySet
->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL
));
933 xFooterPropertySet
->setPropertyValue("CharWeight", uno::makeAny(awt::FontWeight::NORMAL
));
945 std::vector
<svx::ClassificationResult
> SwEditShell::CollectAdvancedClassification()
947 std::vector
<svx::ClassificationResult
> aResult
;
949 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
950 if (!pDocShell
|| !SfxObjectShell::Current())
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
;
972 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aCreator
.makeCategoryNameKey());
973 if (!aValue
.isEmpty())
974 aResult
.push_back({ svx::ClassificationType::CATEGORY
, aValue
, sBlank
, sBlank
});
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())
993 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
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
});
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
)
1012 uno::Reference
<lang::XServiceInfo
> xTextField
;
1013 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
1014 if (!xTextField
->supportsService(DocInfoServiceName
))
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
});
1066 void SwEditShell::SetClassification(const OUString
& rName
, SfxClassificationPolicyType eType
)
1068 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
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
)
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
;
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
;
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())
1167 // Remove all paragraph classification fields.
1170 uno::Reference
<text::XTextField
> xTextField
= lcl_FindParagraphClassificationField(xModel
, xParent
);
1171 if (!xTextField
.is())
1173 lcl_RemoveParagraphMetadataField(xTextField
);
1176 if (aResults
.empty())
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(),
1185 [&](const svx::ClassificationResult
& rResult
)-> bool
1186 { return rResult
.meType
== svx::ClassificationType::PARAGRAPH
; }),
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);
1198 OUString sValue
= rResult
.msName
;
1199 switch (rResult
.meType
)
1201 case svx::ClassificationType::TEXT
:
1203 sKey
= aKeyCreator
.makeNumberedTextKey();
1207 case svx::ClassificationType::CATEGORY
:
1209 if (rResult
.msIdentifier
.isEmpty())
1211 sKey
= aKeyCreator
.makeCategoryNameKey();
1215 sValue
= rResult
.msIdentifier
;
1216 sKey
= aKeyCreator
.makeCategoryIdentifierKey();
1218 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xNodeSubject
, ParagraphClassificationAbbrRDFName
, rResult
.msAbbreviatedName
);
1222 case svx::ClassificationType::MARKING
:
1224 sKey
= aKeyCreator
.makeNumberedMarkingKey();
1228 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
:
1230 sKey
= aKeyCreator
.makeNumberedIntellectualPropertyPartKey();
1238 OUString sDisplayText
= (isFirst
? ("(" + rResult
.msAbbreviatedName
) : rResult
.msAbbreviatedName
);
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
;
1249 for (const OUString
& rFieldName
: aFieldNames
)
1252 sFieldNames
.append("/");
1253 sFieldNames
.append(rFieldName
);
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())
1268 SwTextNode
* pNode
= GetCursor()->Start()->nNode
.GetNode().GetTextNode();
1269 if (pNode
== nullptr)
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())
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
)
1303 uno::Reference
<lang::XServiceInfo
> xField
;
1304 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xField
;
1305 if (!xField
->supportsService(MetadataFieldServiceName
))
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
});
1341 std::vector
<svx::ClassificationResult
> SwEditShell::CollectParagraphClassification()
1343 std::vector
<svx::ClassificationResult
> aResult
;
1345 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1346 if (!pDocShell
|| !GetCursor() || !GetCursor()->Start())
1349 SwTextNode
* pNode
= GetCursor()->Start()->nNode
.GetNode().GetTextNode();
1350 if (pNode
== nullptr)
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
;
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();
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
;
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
);
1414 sal_Int16 nTransparency
;
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
);
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())
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();
1481 if (!bSuccess
|| xWatermark
.is() || bDeleteWatermark
)
1484 const OUString
& sFont
= rWatermark
.GetFont();
1485 sal_Int16 nAngle
= rWatermark
.GetAngle();
1486 sal_Int16 nTransparency
= rWatermark
.GetTransparency();
1487 Color nColor
= rWatermark
.GetColor();
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());
1501 fRatio
= pDevice
->GetTextHeight();
1502 fRatio
/= nTextWidth
;
1506 sal_Int32 nWidth
= 0;
1508 xPageStyle
->getPropertyValue(UNO_NAME_SIZE
) >>= aSize
;
1509 if (aSize
.Width
< aSize
.Height
)
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
;
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)},
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
));
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();
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
;
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
,
1671 : SwUndo(SwUndoId::PARA_SIGN_ADD
, &rDoc
),
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
&)
1700 void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext
&)
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
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())
1755 const SwPosition
* pPosStart
= GetCursor()->Start();
1758 SwTextNode
* pNode
= pPosStart
->nNode
.GetNode().GetTextNode();
1762 // Table text signing is not supported.
1763 if (pNode
->FindTableNode() != nullptr)
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())
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())
1784 svl::crypto::Signing
signing(xCertificate
);
1785 signing
.AddDataRange(utf8Text
.getStr(), utf8Text
.getLength());
1786 OStringBuffer sigBuf
;
1787 if (!signing
.Sign(sigBuf
))
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";
1799 if (it
!= vec
.end())
1800 it
->Value
>>= aUsage
;
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())
1828 // Table text signing is not supported.
1829 if (pNode
->FindTableNode() != nullptr)
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())
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())
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())
1872 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
1873 if (!xParagraphs
.is())
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
);
1909 void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
1911 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1912 if (!pDocShell
|| !IsParagraphSignatureValidationEnabled())
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())
1927 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
1928 if (!xParagraphs
.is())
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())
1950 sal_Int32 nIndex
= 0;
1953 const OUString sCurFieldName
= sFieldNames
.getToken(0, '/', nIndex
);
1954 if (sCurFieldName
.isEmpty())
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
);
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(":");
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
);
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
);
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
);
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
);
2074 lcl_RemoveParagraphMetadataField(xField
);
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
;
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();
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();
2139 // Bail out as early as possible if we don't have paragraph classification.
2140 if (!SwRDFHelper::hasMetadataGraph(pDocShell
->GetBaseModel(), MetaNS
))
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
)));
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());
2183 SetClassification(sHighestClass
, eHighestClassType
);
2188 void SwEditShell::SetTextFormatColl(SwTextFormatColl
*pFormat
,
2189 const bool bResetListAttrs
)
2191 SwTextFormatColl
*pLocal
= pFormat
? pFormat
: (*GetDoc()->GetTextFormatColls())[0];
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())
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
);
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" );
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();
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!)
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
);
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
);
2300 GetDoc()->ChgFormat(*pColl
, aSet
);
2303 GetDoc()->ChgFormat(*pColl
, *pSet
);
2306 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */