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