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 <SwStyleNameMapper.hxx>
92 #include <unoparagraph.hxx>
93 #include <strings.hrc>
95 #include <UndoParagraphSignature.hxx>
97 #include <fmtmeta.hxx>
98 #include <unotxdoc.hxx>
99 #include <unotextbodyhf.hxx>
100 #include <unoport.hxx>
102 #include <comphelper/diagnose_ex.hxx>
103 #include <IDocumentRedlineAccess.hxx>
105 constexpr OUString WATERMARK_NAME
= u
"PowerPlusWaterMarkObject"_ustr
;
106 #define WATERMARK_AUTO_SIZE sal_uInt32(1)
110 constexpr OUString
MetaFilename(u
"tscp/bails.rdf"_ustr
);
111 constexpr OUString
MetaNS(u
"urn:bails"_ustr
);
112 constexpr OUString ParagraphSignatureRDFNamespace
= u
"urn:bails:loext:paragraph:signature:"_ustr
;
113 constexpr OUString ParagraphSignatureIdRDFName
= u
"urn:bails:loext:paragraph:signature:id"_ustr
;
114 constexpr OUString ParagraphSignatureDigestRDFName
= u
":digest"_ustr
;
115 constexpr OUString ParagraphSignatureDateRDFName
= u
":date"_ustr
;
116 constexpr OUString ParagraphSignatureUsageRDFName
= u
":usage"_ustr
;
117 constexpr OUString ParagraphSignatureLastIdRDFName
= u
"urn:bails:loext:paragraph:signature:lastid"_ustr
;
118 constexpr OUString ParagraphClassificationNameRDFName
= u
"urn:bails:loext:paragraph:classification:name"_ustr
;
119 constexpr OUString ParagraphClassificationValueRDFName
= u
"urn:bails:loext:paragraph:classification:value"_ustr
;
120 constexpr OUString ParagraphClassificationAbbrRDFName
= u
"urn:bails:loext:paragraph:classification:abbreviation"_ustr
;
121 constexpr OUString ParagraphClassificationFieldNamesRDFName
= u
"urn:bails:loext:paragraph:classification:fields"_ustr
;
122 constexpr OUString MetadataFieldServiceName
= u
"com.sun.star.text.textfield.MetadataField"_ustr
;
123 constexpr OUString DocInfoServiceName
= u
"com.sun.star.text.TextField.DocInfo.Custom"_ustr
;
125 /// Find all page styles which are currently used in the document.
126 std::vector
<OUString
> lcl_getUsedPageStyles(SwViewShell
const * pShell
)
128 std::vector
<OUString
> aReturn
;
130 SwRootFrame
* pLayout
= pShell
->GetLayout();
131 for (SwFrame
* pFrame
= pLayout
->GetLower(); pFrame
; pFrame
= pFrame
->GetNext())
133 SwPageFrame
* pPage
= static_cast<SwPageFrame
*>(pFrame
);
134 if (const SwPageDesc
*pDesc
= pPage
->FindPageDesc())
137 SwStyleNameMapper::FillProgName(pDesc
->GetName(), sStyleName
, SwGetPoolIdFromName::PageDesc
);
138 aReturn
.push_back(sStyleName
);
145 /// Search for a field named rFieldName of type rServiceName in xText and return it.
146 uno::Reference
<text::XTextField
> lcl_findField(const uno::Reference
<text::XText
>& xText
, const OUString
& rServiceName
, std::u16string_view rFieldName
)
148 uno::Reference
<text::XTextField
> xField
;
149 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xText
, uno::UNO_QUERY
);
150 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
151 while (xParagraphs
->hasMoreElements())
153 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraphs
->nextElement(), uno::UNO_QUERY
);
154 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
155 while (xTextPortions
->hasMoreElements())
157 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
158 OUString aTextPortionType
;
159 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
160 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
163 uno::Reference
<lang::XServiceInfo
> xTextField
;
164 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
165 if (!xTextField
->supportsService(rServiceName
))
169 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextField
, uno::UNO_QUERY
);
170 xPropertySet
->getPropertyValue(UNO_NAME_NAME
) >>= aName
;
171 if (aName
== rFieldName
)
173 xField
= uno::Reference
<text::XTextField
>(xTextField
, uno::UNO_QUERY
);
182 /// Search for a field named rFieldName of type rServiceName in xText and return true iff found.
183 bool lcl_hasField(const uno::Reference
<text::XText
>& xText
, const OUString
& rServiceName
, std::u16string_view rFieldName
)
185 return lcl_findField(xText
, rServiceName
, rFieldName
).is();
188 /// Search for a frame with WATERMARK_NAME in name of type rServiceName in xText. Returns found name in rShapeName.
189 uno::Reference
<drawing::XShape
> lcl_getWatermark(const uno::Reference
<text::XText
>& xText
,
190 const OUString
& rServiceName
, OUString
& rShapeName
, bool& bSuccess
)
193 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xText
, uno::UNO_QUERY
);
194 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
195 while (xParagraphs
->hasMoreElements())
197 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraphs
->nextElement(), uno::UNO_QUERY
);
198 if (!xTextPortionEnumerationAccess
.is())
203 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
204 while (xTextPortions
->hasMoreElements())
206 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
207 OUString aTextPortionType
;
208 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
209 if (aTextPortionType
!= "Frame")
212 uno::Reference
<container::XContentEnumerationAccess
> xContentEnumerationAccess(xTextPortion
, uno::UNO_QUERY
);
213 if (!xContentEnumerationAccess
.is())
216 uno::Reference
<container::XEnumeration
> xEnumeration
= xContentEnumerationAccess
->createContentEnumeration(u
"com.sun.star.text.TextContent"_ustr
);
217 if (!xEnumeration
->hasMoreElements())
220 uno::Reference
<lang::XServiceInfo
> xWatermark(xEnumeration
->nextElement(), uno::UNO_QUERY
);
221 if (!xWatermark
->supportsService(rServiceName
))
224 uno::Reference
<container::XNamed
> xNamed(xWatermark
, uno::UNO_QUERY
);
226 if (!xNamed
->getName().match(WATERMARK_NAME
))
229 rShapeName
= xNamed
->getName();
231 uno::Reference
<drawing::XShape
> xShape(xWatermark
, uno::UNO_QUERY
);
236 return uno::Reference
<drawing::XShape
>();
239 /// Extract the text of the paragraph without any of the fields.
240 /// TODO: Consider moving to SwTextNode, or extend ModelToViewHelper.
241 OString
lcl_getParagraphBodyText(const uno::Reference
<text::XTextContent
>& xText
)
243 OUStringBuffer strBuf
;
244 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xText
, uno::UNO_QUERY
);
245 if (!xTextPortionEnumerationAccess
.is())
248 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
249 while (xTextPortions
->hasMoreElements())
251 uno::Any elem
= xTextPortions
->nextElement();
253 //TODO: Consider including hidden and conditional texts/portions.
254 OUString aTextPortionType
;
255 uno::Reference
<beans::XPropertySet
> xPropertySet(elem
, uno::UNO_QUERY
);
256 xPropertySet
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
257 if (aTextPortionType
== "Text")
259 uno::Reference
<text::XTextRange
> xTextRange(elem
, uno::UNO_QUERY
);
261 strBuf
.append(xTextRange
->getString());
265 // Cleanup the dummy characters added by fields (which we exclude).
266 comphelper::string::remove(strBuf
, CH_TXT_ATR_INPUTFIELDSTART
);
267 comphelper::string::remove(strBuf
, CH_TXT_ATR_INPUTFIELDEND
);
268 comphelper::string::remove(strBuf
, CH_TXTATR_BREAKWORD
);
270 return strBuf
.makeStringAndClear().trim().toUtf8();
273 template <typename T
>
274 std::map
<OUString
, OUString
> lcl_getRDFStatements(const rtl::Reference
<SwXTextDocument
>& xModel
,
279 const css::uno::Reference
<css::rdf::XResource
> xSubject(xRef
, uno::UNO_QUERY
);
280 return SwRDFHelper::getStatements(xModel
, MetaNS
, xSubject
);
282 catch (const ::css::uno::Exception
&)
286 return std::map
<OUString
, OUString
>();
289 /// Returns RDF (key, value) pair associated with the field, if any.
290 std::pair
<OUString
, OUString
> lcl_getFieldRDFByPrefix(const rtl::Reference
<SwXTextDocument
>& xModel
,
291 const uno::Reference
<css::text::XTextField
>& xField
,
292 std::u16string_view sPrefix
)
294 for (const auto& pair
: lcl_getRDFStatements(xModel
, xField
))
296 if (pair
.first
.startsWith(sPrefix
))
300 return std::make_pair(OUString(), OUString());
303 /// Returns RDF (key, value) pair associated with the field, if any.
304 template <typename T
>
305 std::pair
<OUString
, OUString
> lcl_getRDF(const rtl::Reference
<SwXTextDocument
>& xModel
,
307 const OUString
& sRDFName
)
309 const std::map
<OUString
, OUString
> aStatements
= lcl_getRDFStatements(xModel
, xRef
);
310 const auto it
= aStatements
.find(sRDFName
);
311 return (it
!= aStatements
.end()) ? std::make_pair(it
->first
, it
->second
) : std::make_pair(OUString(), OUString());
314 /// Returns true iff the field in question is paragraph signature.
315 /// Note: must have associated RDF, since signatures are otherwise just metadata fields.
316 bool lcl_IsParagraphSignatureField(const rtl::Reference
<SwXTextDocument
>& xModel
,
317 const uno::Reference
<css::text::XTextField
>& xField
)
319 return (lcl_getRDF(xModel
, xField
, ParagraphSignatureIdRDFName
).first
== ParagraphSignatureIdRDFName
);
322 uno::Reference
<text::XTextField
> lcl_findFieldByRDF(const rtl::Reference
<SwXTextDocument
>& xModel
,
323 const uno::Reference
<text::XTextContent
>& xParagraph
,
324 const OUString
& sRDFName
,
325 std::u16string_view sRDFValue
)
327 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraph
, uno::UNO_QUERY
);
328 if (!xTextPortionEnumerationAccess
.is())
329 return uno::Reference
<text::XTextField
>();
331 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
332 if (!xTextPortions
.is())
333 return uno::Reference
<text::XTextField
>();
335 while (xTextPortions
->hasMoreElements())
337 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
338 OUString aTextPortionType
;
339 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
340 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
343 uno::Reference
<lang::XServiceInfo
> xTextField
;
344 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
345 if (!xTextField
->supportsService(MetadataFieldServiceName
))
348 uno::Reference
<text::XTextField
> xField(xTextField
, uno::UNO_QUERY
);
349 const std::pair
<OUString
, OUString
> pair
= lcl_getRDF(xModel
, xField
, sRDFName
);
350 if (pair
.first
== sRDFName
&& (sRDFValue
.empty() || sRDFValue
== pair
.second
))
354 return uno::Reference
<text::XTextField
>();
357 struct SignatureDescr
359 OUString msSignature
;
363 bool isValid() const { return !msSignature
.isEmpty(); }
366 SignatureDescr
lcl_getSignatureDescr(const rtl::Reference
<SwXTextDocument
>& xModel
,
367 const uno::Reference
<css::text::XTextContent
>& xParagraph
,
368 std::u16string_view sFieldId
)
370 SignatureDescr aDescr
;
372 const OUString prefix
= ParagraphSignatureRDFNamespace
+ sFieldId
;
373 const std::map
<OUString
, OUString
> aStatements
= lcl_getRDFStatements(xModel
, xParagraph
);
375 const auto itSig
= aStatements
.find(prefix
+ ParagraphSignatureDigestRDFName
);
376 aDescr
.msSignature
= (itSig
!= aStatements
.end() ? itSig
->second
: OUString());
378 const auto itDate
= aStatements
.find(prefix
+ ParagraphSignatureDateRDFName
);
379 aDescr
.msDate
= (itDate
!= aStatements
.end() ? itDate
->second
: OUString());
381 const auto itUsage
= aStatements
.find(prefix
+ ParagraphSignatureUsageRDFName
);
382 aDescr
.msUsage
= (itUsage
!= aStatements
.end() ? itUsage
->second
: OUString());
387 SignatureDescr
lcl_getSignatureDescr(const rtl::Reference
<SwXTextDocument
>& xModel
,
388 const uno::Reference
<css::text::XTextContent
>& xParagraph
,
389 const uno::Reference
<css::text::XTextField
>& xField
)
391 const OUString sFieldId
= lcl_getRDF(xModel
, xField
, ParagraphSignatureIdRDFName
).second
;
392 if (!sFieldId
.isEmpty())
393 return lcl_getSignatureDescr(xModel
, xParagraph
, sFieldId
);
395 return SignatureDescr();
398 /// Validate and create the signature field display text from the fields.
399 std::pair
<bool, OUString
> lcl_MakeParagraphSignatureFieldText(const SignatureDescr
& aDescr
,
400 const OString
& utf8Text
)
402 OUString msg
= SwResId(STR_INVALID_SIGNATURE
);
405 if (aDescr
.isValid())
407 const char* pData
= utf8Text
.getStr();
408 const std::vector
<unsigned char> data(pData
, pData
+ utf8Text
.getLength());
410 OString encSignature
;
411 if (aDescr
.msSignature
.convertToString(&encSignature
, RTL_TEXTENCODING_UTF8
, 0))
413 const std::vector
<unsigned char> sig(svl::crypto::DecodeHexString(encSignature
));
414 SignatureInformation
aInfo(0);
415 valid
= svl::crypto::Signing::Verify(data
, false, sig
, aInfo
);
417 && aInfo
.nStatus
== xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED
;
419 assert(aInfo
.GetSigningCertificate()); // it was valid
420 msg
= SwResId(STR_SIGNED_BY
) + ": " + aInfo
.GetSigningCertificate()->X509Subject
+ ", " +
422 msg
+= (!aDescr
.msUsage
.isEmpty() ? (" (" + aDescr
.msUsage
+ "): ") : u
": "_ustr
);
423 msg
+= (valid
? SwResId(STR_VALID
) : SwResId(STR_INVALID
));
427 return std::make_pair(valid
, msg
);
430 /// Validate and return validation result and signature field display text.
431 std::pair
<bool, OUString
>
432 lcl_MakeParagraphSignatureFieldText(const rtl::Reference
<SwXTextDocument
>& xModel
,
433 const uno::Reference
<css::text::XTextContent
>& xParagraph
,
434 const uno::Reference
<css::text::XTextField
>& xField
,
435 const OString
& utf8Text
)
437 const SignatureDescr aDescr
= lcl_getSignatureDescr(xModel
, xParagraph
, xField
);
438 return lcl_MakeParagraphSignatureFieldText(aDescr
, utf8Text
);
441 /// Generate the next valid ID for the new signature on this paragraph.
442 OUString
lcl_getNextSignatureId(const rtl::Reference
<SwXTextDocument
>& xModel
,
443 const uno::Reference
<text::XTextContent
>& xParagraph
)
445 const OUString sFieldId
= lcl_getRDF(xModel
, xParagraph
, ParagraphSignatureLastIdRDFName
).second
;
446 return OUString::number(!sFieldId
.isEmpty() ? sFieldId
.toInt32() + 1 : 1);
449 /// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
450 uno::Reference
<text::XTextField
> lcl_InsertParagraphSignature(const rtl::Reference
<SwXTextDocument
>& xModel
,
451 const uno::Reference
<text::XTextContent
>& xParagraph
,
452 const OUString
& signature
,
453 const OUString
& usage
)
455 uno::Reference
<lang::XMultiServiceFactory
> xMultiServiceFactory(xModel
);
456 auto xField
= uno::Reference
<text::XTextField
>(xMultiServiceFactory
->createInstance(MetadataFieldServiceName
), uno::UNO_QUERY
);
458 // Add the signature at the end.
459 xField
->attach(xParagraph
->getAnchor()->getEnd());
461 const OUString sId
= lcl_getNextSignatureId(xModel
, xParagraph
);
463 const css::uno::Reference
<css::rdf::XResource
> xSubject(xField
, uno::UNO_QUERY
);
464 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xSubject
, ParagraphSignatureIdRDFName
, sId
);
466 // First convert the UTC UNIX timestamp to a tools::DateTime then to local time.
467 DateTime aDateTime
= DateTime::CreateFromUnixTime(time(nullptr));
468 aDateTime
.ConvertToLocalTime();
469 OUStringBuffer rBuffer
;
470 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetYear()));
472 if (aDateTime
.GetMonth() < 10)
474 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetMonth()));
476 if (aDateTime
.GetDay() < 10)
478 rBuffer
.append(static_cast<sal_Int32
>(aDateTime
.GetDay()));
480 // Now set the RDF on the paragraph, since that's what is preserved in .doc(x).
481 const css::uno::Reference
<css::rdf::XResource
> xParaSubject(xParagraph
, uno::UNO_QUERY
);
482 const OUString prefix
= ParagraphSignatureRDFNamespace
+ sId
;
483 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xParaSubject
, ParagraphSignatureLastIdRDFName
, sId
);
484 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xParaSubject
, prefix
+ ParagraphSignatureDigestRDFName
, signature
);
485 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xParaSubject
, prefix
+ ParagraphSignatureUsageRDFName
, usage
);
486 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xParaSubject
, prefix
+ ParagraphSignatureDateRDFName
, rBuffer
.makeStringAndClear());
491 /// Updates the signature field text if changed and returns true only iff updated.
492 bool lcl_DoUpdateParagraphSignatureField(SwDoc
& rDoc
,
493 const uno::Reference
<css::text::XTextField
>& xField
,
494 const OUString
& sDisplayText
)
496 // Disable undo to avoid introducing noise when we edit the metadata field.
497 const bool isUndoEnabled
= rDoc
.GetIDocumentUndoRedo().DoesUndo();
498 rDoc
.GetIDocumentUndoRedo().DoUndo(false);
499 comphelper::ScopeGuard
const g([&rDoc
, isUndoEnabled
]() {
500 rDoc
.GetIDocumentUndoRedo().DoUndo(isUndoEnabled
);
505 uno::Reference
<css::text::XTextRange
> xText(xField
, uno::UNO_QUERY
);
506 const OUString curText
= xText
->getString();
507 if (curText
!= sDisplayText
)
509 xText
->setString(sDisplayText
);
513 catch (const uno::Exception
&)
515 // We failed; avoid crashing.
516 DBG_UNHANDLED_EXCEPTION("sw.uno", "Failed to update paragraph signature");
522 /// Updates the signature field text if changed and returns true only iff updated.
523 bool lcl_UpdateParagraphSignatureField(SwDoc
& rDoc
,
524 const rtl::Reference
<SwXTextDocument
>& xModel
,
525 const uno::Reference
<css::text::XTextContent
>& xParagraph
,
526 const uno::Reference
<css::text::XTextField
>& xField
,
527 const OString
& utf8Text
)
529 const OUString sDisplayText
530 = lcl_MakeParagraphSignatureFieldText(xModel
, xParagraph
, xField
, utf8Text
).second
;
531 return lcl_DoUpdateParagraphSignatureField(rDoc
, xField
, sDisplayText
);
534 void lcl_RemoveParagraphMetadataField(const uno::Reference
<css::text::XTextField
>& xField
)
536 uno::Reference
<css::text::XTextRange
> xParagraph(xField
->getAnchor());
537 xParagraph
->getText()->removeTextContent(xField
);
540 /// Returns true iff the field in question is paragraph classification.
541 /// Note: must have associated RDF, since classifications are otherwise just metadata fields.
542 bool lcl_IsParagraphClassificationField(const rtl::Reference
<SwXTextDocument
>& xModel
,
543 const uno::Reference
<css::text::XTextField
>& xField
,
544 std::u16string_view sKey
)
546 const std::pair
<OUString
, OUString
> rdfPair
= lcl_getRDF(xModel
, xField
, ParagraphClassificationNameRDFName
);
547 return rdfPair
.first
== ParagraphClassificationNameRDFName
&& (sKey
.empty() || rdfPair
.second
== sKey
);
550 uno::Reference
<text::XTextField
> lcl_FindParagraphClassificationField(const rtl::Reference
<SwXTextDocument
>& xModel
,
551 const rtl::Reference
<SwXParagraph
>& xParagraph
,
552 std::u16string_view sKey
= u
"")
554 uno::Reference
<text::XTextField
> xTextField
;
556 if (!xParagraph
.is())
559 // Enumerate text portions to find metadata fields. This is expensive, best to enumerate fields only.
560 rtl::Reference
<SwXTextPortionEnumeration
> xTextPortions
= xParagraph
->createTextFieldsEnumeration();
561 while (xTextPortions
->hasMoreElements())
563 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
564 OUString aTextPortionType
;
565 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
566 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
569 uno::Reference
<lang::XServiceInfo
> xServiceInfo
;
570 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xServiceInfo
;
571 if (!xServiceInfo
->supportsService(MetadataFieldServiceName
))
574 uno::Reference
<text::XTextField
> xField(xServiceInfo
, uno::UNO_QUERY
);
575 if (lcl_IsParagraphClassificationField(xModel
, xField
, sKey
))
577 xTextField
= std::move(xField
);
585 /// Creates and inserts Paragraph Classification Metadata field and creates the RDF entry
586 uno::Reference
<text::XTextField
> lcl_InsertParagraphClassification(const rtl::Reference
<SwXTextDocument
>& xModel
,
587 const uno::Reference
<text::XTextContent
>& xParent
)
589 auto xField
= uno::Reference
<text::XTextField
>(xModel
->createInstance(MetadataFieldServiceName
), uno::UNO_QUERY
);
591 // Add the classification at the start.
592 xField
->attach(xParent
->getAnchor()->getStart());
596 /// Updates the paragraph classification field text if changed and returns true only iff updated.
597 bool lcl_UpdateParagraphClassificationField(SwDoc
* pDoc
,
598 const rtl::Reference
<SwXTextDocument
>& xModel
,
599 const uno::Reference
<css::text::XTextContent
>& xTextNode
,
600 const OUString
& sKey
,
601 const OUString
& sValue
,
602 const OUString
& sDisplayText
)
604 // Disable undo to avoid introducing noise when we edit the metadata field.
605 const bool isUndoEnabled
= pDoc
->GetIDocumentUndoRedo().DoesUndo();
606 pDoc
->GetIDocumentUndoRedo().DoUndo(false);
607 comphelper::ScopeGuard
const g([pDoc
, isUndoEnabled
] () {
608 pDoc
->GetIDocumentUndoRedo().DoUndo(isUndoEnabled
);
611 uno::Reference
<text::XTextField
> xField
= lcl_InsertParagraphClassification(xModel
, xTextNode
);
613 css::uno::Reference
<css::rdf::XResource
> xFieldSubject(xField
, uno::UNO_QUERY
);
614 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xFieldSubject
, sKey
, sValue
);
615 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xFieldSubject
, ParagraphClassificationNameRDFName
, sKey
);
616 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xFieldSubject
, ParagraphClassificationValueRDFName
, sValue
);
618 css::uno::Reference
<css::rdf::XResource
> xNodeSubject(xTextNode
, uno::UNO_QUERY
);
619 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xNodeSubject
, sKey
, sValue
);
621 return lcl_DoUpdateParagraphSignatureField(*pDoc
, xField
, sDisplayText
);
624 void lcl_ValidateParagraphSignatures(SwDoc
& rDoc
, const uno::Reference
<text::XTextContent
>& xParagraph
, const bool updateDontRemove
, const uno::Sequence
<uno::Reference
<css::rdf::XURI
>>& rGraphNames
)
626 SwDocShell
* pDocShell
= rDoc
.GetDocShell();
630 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
632 // Check if the paragraph is signed.
635 const css::uno::Reference
<css::rdf::XResource
> xSubject(xParagraph
, uno::UNO_QUERY
);
636 std::map
<OUString
, OUString
> aStatements
= SwRDFHelper::getStatements(xModel
, rGraphNames
, xSubject
);
637 const auto it
= aStatements
.find(ParagraphSignatureLastIdRDFName
);
638 if (it
== aStatements
.end() || it
->second
.isEmpty())
641 catch (const ::css::uno::Exception
&)
646 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraph
, uno::UNO_QUERY
);
647 if (!xTextPortionEnumerationAccess
.is())
650 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
651 if (!xTextPortions
.is())
654 // Get the text (without fields).
655 const OString utf8Text
= lcl_getParagraphBodyText(xParagraph
);
656 if (utf8Text
.isEmpty())
659 while (xTextPortions
->hasMoreElements())
661 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
662 OUString aTextPortionType
;
663 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
664 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
667 uno::Reference
<lang::XServiceInfo
> xTextField
;
668 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
669 if (!xTextField
->supportsService(MetadataFieldServiceName
))
672 uno::Reference
<text::XTextField
> xField(xTextField
, uno::UNO_QUERY
);
673 if (!lcl_IsParagraphSignatureField(xModel
, xField
))
678 if (updateDontRemove
)
680 lcl_UpdateParagraphSignatureField(rDoc
, xModel
, xParagraph
, xField
, utf8Text
);
682 else if (!lcl_MakeParagraphSignatureFieldText(xModel
, xParagraph
, xField
, utf8Text
).first
)
684 rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD
, nullptr);
685 rDoc
.GetIDocumentUndoRedo().AppendUndo(
686 std::make_unique
<SwUndoParagraphSigning
>(rDoc
, xField
, xParagraph
, false));
687 lcl_RemoveParagraphMetadataField(xField
);
688 rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD
, nullptr);
693 } // anonymous namespace
695 SwTextFormatColl
& SwEditShell::GetDfltTextFormatColl() const
697 return *GetDoc()->GetDfltTextFormatColl();
700 sal_uInt16
SwEditShell::GetTextFormatCollCount() const
702 return GetDoc()->GetTextFormatColls()->size();
705 SwTextFormatColl
& SwEditShell::GetTextFormatColl(sal_uInt16 nFormatColl
) const
707 return *((*(GetDoc()->GetTextFormatColls()))[nFormatColl
]);
710 static void insertFieldToDocument(uno::Reference
<lang::XMultiServiceFactory
> const & rxMultiServiceFactory
,
711 uno::Reference
<text::XText
> const & rxText
, uno::Reference
<text::XParagraphCursor
> const & rxParagraphCursor
,
712 OUString
const & rsKey
)
714 uno::Reference
<beans::XPropertySet
> xField(rxMultiServiceFactory
->createInstance(DocInfoServiceName
), uno::UNO_QUERY
);
715 xField
->setPropertyValue(UNO_NAME_NAME
, uno::Any(rsKey
));
716 uno::Reference
<text::XTextContent
> xTextContent(xField
, uno::UNO_QUERY
);
718 rxText
->insertTextContent(rxParagraphCursor
, xTextContent
, false);
721 static void removeAllClassificationFields(std::u16string_view rPolicy
, uno::Reference
<text::XText
> const & rxText
)
723 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(rxText
, uno::UNO_QUERY
);
724 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
725 while (xParagraphs
->hasMoreElements())
727 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraphs
->nextElement(), uno::UNO_QUERY
);
728 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
729 while (xTextPortions
->hasMoreElements())
731 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
732 OUString aTextPortionType
;
733 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
734 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
737 uno::Reference
<lang::XServiceInfo
> xTextField
;
738 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
739 if (!xTextField
->supportsService(DocInfoServiceName
))
743 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextField
, uno::UNO_QUERY
);
744 xPropertySet
->getPropertyValue(UNO_NAME_NAME
) >>= aName
;
745 if (aName
.startsWith(rPolicy
))
747 uno::Reference
<text::XTextField
> xField(xTextField
, uno::UNO_QUERY
);
748 rxText
->removeTextContent(xField
);
754 static sal_Int32
getNumberOfParagraphs(uno::Reference
<text::XText
> const & xText
)
756 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumAccess(xText
, uno::UNO_QUERY
);
757 uno::Reference
<container::XEnumeration
> xParagraphEnum
= xParagraphEnumAccess
->createEnumeration();
758 sal_Int32 nResult
= 0;
759 while (xParagraphEnum
->hasMoreElements())
761 xParagraphEnum
->nextElement();
767 static void equaliseNumberOfParagraph(std::vector
<svx::ClassificationResult
> const & rResults
, uno::Reference
<text::XText
> const & xText
)
769 sal_Int32 nNumberOfParagraphs
= 0;
770 for (svx::ClassificationResult
const & rResult
: rResults
)
772 if (rResult
.meType
== svx::ClassificationType::PARAGRAPH
)
773 nNumberOfParagraphs
++;
776 while (getNumberOfParagraphs(xText
) < nNumberOfParagraphs
)
778 uno::Reference
<text::XParagraphAppend
> xParagraphAppend(xText
, uno::UNO_QUERY
);
779 xParagraphAppend
->finishParagraph(uno::Sequence
<beans::PropertyValue
>());
783 void SwEditShell::ApplyAdvancedClassification(std::vector
<svx::ClassificationResult
> const & rResults
)
785 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
789 const SfxObjectShell
* pObjSh
= SfxObjectShell::Current();
793 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
794 uno::Reference
<container::XNameAccess
> xStyleFamilies
= xModel
->getStyleFamilies();
795 uno::Reference
<container::XNameAccess
> xStyleFamily(xStyleFamilies
->getByName(u
"PageStyles"_ustr
), uno::UNO_QUERY
);
797 uno::Reference
<lang::XMultiServiceFactory
> xMultiServiceFactory(xModel
);
799 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
= pObjSh
->getDocProperties();
801 const OUString sPolicy
= SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
802 const std::vector
<OUString
> aUsedPageStyles
= lcl_getUsedPageStyles(this);
803 for (const OUString
& rPageStyleName
: aUsedPageStyles
)
805 uno::Reference
<beans::XPropertySet
> xPageStyle(xStyleFamily
->getByName(rPageStyleName
), uno::UNO_QUERY
);
808 bool bHeaderIsOn
= false;
809 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
810 uno::Reference
<text::XText
> xHeaderText
;
812 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
813 if (xHeaderText
.is())
814 removeAllClassificationFields(sPolicy
, xHeaderText
);
817 bool bFooterIsOn
= false;
818 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_IS_ON
) >>= bFooterIsOn
;
819 uno::Reference
<text::XText
> xFooterText
;
821 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_TEXT
) >>= xFooterText
;
822 if (xFooterText
.is())
823 removeAllClassificationFields(sPolicy
, xFooterText
);
827 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
= xDocumentProperties
->getUserDefinedProperties();
828 svx::classification::removeAllProperties(xPropertyContainer
);
830 SfxClassificationHelper
aHelper(xDocumentProperties
);
832 // Apply properties from the BA policy
833 for (svx::ClassificationResult
const & rResult
: rResults
)
835 if (rResult
.meType
== svx::ClassificationType::CATEGORY
)
837 aHelper
.SetBACName(rResult
.msName
, SfxClassificationHelper::getPolicyType());
841 sfx::ClassificationKeyCreator
aCreator(SfxClassificationHelper::getPolicyType());
843 // Insert origin document property
844 svx::classification::insertCreationOrigin(xPropertyContainer
, aCreator
, sfx::ClassificationCreationOrigin::MANUAL
);
846 // Insert full text as document property
847 svx::classification::insertFullTextualRepresentationAsDocumentProperty(xPropertyContainer
, aCreator
, rResults
);
849 for (const OUString
& rPageStyleName
: aUsedPageStyles
)
851 uno::Reference
<beans::XPropertySet
> xPageStyle(xStyleFamily
->getByName(rPageStyleName
), uno::UNO_QUERY
);
854 bool bHeaderIsOn
= false;
855 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
857 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_IS_ON
, uno::Any(true));
858 uno::Reference
<text::XText
> xHeaderText
;
859 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
860 equaliseNumberOfParagraph(rResults
, xHeaderText
);
863 bool bFooterIsOn
= false;
864 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_IS_ON
) >>= bFooterIsOn
;
866 xPageStyle
->setPropertyValue(UNO_NAME_FOOTER_IS_ON
, uno::Any(true));
867 uno::Reference
<text::XText
> xFooterText
;
868 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_TEXT
) >>= xFooterText
;
869 equaliseNumberOfParagraph(rResults
, xFooterText
);
871 // SET/DELETE WATERMARK
872 SfxWatermarkItem aWatermarkItem
;
873 aWatermarkItem
.SetText(aHelper
.GetDocumentWatermark());
874 SetWatermark(aWatermarkItem
);
876 uno::Reference
<text::XParagraphCursor
> xHeaderParagraphCursor(xHeaderText
->createTextCursor(), uno::UNO_QUERY
);
877 uno::Reference
<text::XParagraphCursor
> xFooterParagraphCursor(xFooterText
->createTextCursor(), uno::UNO_QUERY
);
879 sal_Int32 nParagraph
= -1;
881 for (svx::ClassificationResult
const & rResult
: rResults
)
883 switch(rResult
.meType
)
885 case svx::ClassificationType::TEXT
:
887 OUString sKey
= aCreator
.makeNumberedTextKey();
889 svx::classification::addOrInsertDocumentProperty(xPropertyContainer
, sKey
, rResult
.msName
);
890 insertFieldToDocument(xMultiServiceFactory
, xHeaderText
, xHeaderParagraphCursor
, sKey
);
891 insertFieldToDocument(xMultiServiceFactory
, xFooterText
, xFooterParagraphCursor
, sKey
);
895 case svx::ClassificationType::CATEGORY
:
897 OUString sKey
= aCreator
.makeCategoryNameKey();
898 insertFieldToDocument(xMultiServiceFactory
, xHeaderText
, xHeaderParagraphCursor
, sKey
);
899 insertFieldToDocument(xMultiServiceFactory
, xFooterText
, xFooterParagraphCursor
, sKey
);
903 case svx::ClassificationType::MARKING
:
905 OUString sKey
= aCreator
.makeNumberedMarkingKey();
906 svx::classification::addOrInsertDocumentProperty(xPropertyContainer
, sKey
, rResult
.msName
);
907 insertFieldToDocument(xMultiServiceFactory
, xHeaderText
, xHeaderParagraphCursor
, sKey
);
908 insertFieldToDocument(xMultiServiceFactory
, xFooterText
, xFooterParagraphCursor
, sKey
);
912 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
:
914 OUString sKey
= aCreator
.makeNumberedIntellectualPropertyPartKey();
915 svx::classification::addOrInsertDocumentProperty(xPropertyContainer
, sKey
, rResult
.msName
);
916 insertFieldToDocument(xMultiServiceFactory
, xHeaderText
, xHeaderParagraphCursor
, sKey
);
917 insertFieldToDocument(xMultiServiceFactory
, xFooterText
, xFooterParagraphCursor
, sKey
);
921 case svx::ClassificationType::PARAGRAPH
:
925 if (nParagraph
!= 0) // only jump to next paragraph, if we aren't at the first paragraph
927 xHeaderParagraphCursor
->gotoNextParagraph(false);
928 xFooterParagraphCursor
->gotoNextParagraph(false);
931 xHeaderParagraphCursor
->gotoStartOfParagraph(false);
932 xFooterParagraphCursor
->gotoStartOfParagraph(false);
934 uno::Reference
<beans::XPropertySet
> xHeaderPropertySet(xHeaderParagraphCursor
, uno::UNO_QUERY_THROW
);
935 uno::Reference
<beans::XPropertySet
> xFooterPropertySet(xFooterParagraphCursor
, uno::UNO_QUERY_THROW
);
936 if (rResult
.msName
== "BOLD")
938 xHeaderPropertySet
->setPropertyValue(u
"CharWeight"_ustr
, uno::Any(awt::FontWeight::BOLD
));
939 xFooterPropertySet
->setPropertyValue(u
"CharWeight"_ustr
, uno::Any(awt::FontWeight::BOLD
));
943 xHeaderPropertySet
->setPropertyValue(u
"CharWeight"_ustr
, uno::Any(awt::FontWeight::NORMAL
));
944 xFooterPropertySet
->setPropertyValue(u
"CharWeight"_ustr
, uno::Any(awt::FontWeight::NORMAL
));
956 std::vector
<svx::ClassificationResult
> SwEditShell::CollectAdvancedClassification()
958 std::vector
<svx::ClassificationResult
> aResult
;
960 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
964 const SfxObjectShell
* pObjSh
= SfxObjectShell::Current();
968 const OUString sBlank
;
970 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
= pObjSh
->getDocProperties();
971 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
= xDocumentProperties
->getUserDefinedProperties();
972 sfx::ClassificationKeyCreator
aCreator(SfxClassificationHelper::getPolicyType());
974 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
975 uno::Reference
<container::XNameAccess
> xStyleFamilies
= xModel
->getStyleFamilies();
976 uno::Reference
<container::XNameAccess
> xStyleFamily(xStyleFamilies
->getByName(u
"PageStyles"_ustr
), uno::UNO_QUERY
);
978 std::vector
<OUString
> aPageStyles
= lcl_getUsedPageStyles(this);
979 const OUString
& aPageStyleString
= aPageStyles
.back();
980 uno::Reference
<beans::XPropertySet
> xPageStyle(xStyleFamily
->getByName(aPageStyleString
), uno::UNO_QUERY
);
982 bool bHeaderIsOn
= false;
983 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
986 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aCreator
.makeCategoryNameKey());
987 if (!aValue
.isEmpty())
988 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, aValue
, sBlank
, sBlank
);
993 uno::Reference
<text::XText
> xHeaderText
;
994 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
996 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xHeaderText
, uno::UNO_QUERY
);
997 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
999 // set to true if category was found in the header
1000 bool bFoundClassificationCategory
= false;
1002 while (xParagraphs
->hasMoreElements())
1004 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraphs
->nextElement(), uno::UNO_QUERY
);
1005 if (!xTextPortionEnumerationAccess
.is())
1007 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
1009 // Check font weight
1010 uno::Reference
<beans::XPropertySet
> xParagraphPropertySet(xTextPortionEnumerationAccess
, uno::UNO_QUERY_THROW
);
1011 uno::Any aAny
= xParagraphPropertySet
->getPropertyValue(u
"CharWeight"_ustr
);
1013 OUString sWeight
= (aAny
.get
<float>() >= awt::FontWeight::BOLD
) ? u
"BOLD"_ustr
: u
"NORMAL"_ustr
;
1015 aResult
.emplace_back(svx::ClassificationType::PARAGRAPH
, sWeight
, sBlank
, sBlank
);
1018 while (xTextPortions
->hasMoreElements())
1020 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
1021 OUString aTextPortionType
;
1022 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
1023 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
1026 uno::Reference
<lang::XServiceInfo
> xTextField
;
1027 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xTextField
;
1028 if (!xTextField
->supportsService(DocInfoServiceName
))
1032 uno::Reference
<beans::XPropertySet
> xPropertySet(xTextField
, uno::UNO_QUERY
);
1033 xPropertySet
->getPropertyValue(UNO_NAME_NAME
) >>= aName
;
1035 if (aCreator
.isMarkingTextKey(aName
))
1037 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aName
);
1038 if (!aValue
.isEmpty())
1039 aResult
.emplace_back(svx::ClassificationType::TEXT
, aValue
, sBlank
, sBlank
);
1041 else if (aCreator
.isCategoryNameKey(aName
))
1043 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aName
);
1044 if (!aValue
.isEmpty())
1045 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, aValue
, sBlank
, sBlank
);
1046 bFoundClassificationCategory
= true;
1048 else if (aCreator
.isCategoryIdentifierKey(aName
))
1050 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aName
);
1051 if (!aValue
.isEmpty())
1052 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, sBlank
, sBlank
, aValue
);
1053 bFoundClassificationCategory
= true;
1055 else if (aCreator
.isMarkingKey(aName
))
1057 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aName
);
1058 if (!aValue
.isEmpty())
1059 aResult
.emplace_back(svx::ClassificationType::MARKING
, aValue
, sBlank
, sBlank
);
1061 else if (aCreator
.isIntellectualPropertyPartKey(aName
))
1063 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aName
);
1064 if (!aValue
.isEmpty())
1065 aResult
.emplace_back(svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
, aValue
, sBlank
, sBlank
);
1070 if (!bFoundClassificationCategory
)
1072 const OUString aValue
= svx::classification::getProperty(xPropertyContainer
, aCreator
.makeCategoryNameKey());
1073 if (!aValue
.isEmpty())
1074 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, aValue
, sBlank
, sBlank
);
1080 void SwEditShell::SetClassification(const OUString
& rName
, SfxClassificationPolicyType eType
)
1082 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1086 SfxClassificationHelper
aHelper(pDocShell
->getDocProperties());
1088 const bool bHadWatermark
= !aHelper
.GetDocumentWatermark().isEmpty();
1090 // This updates the infobar as well.
1091 aHelper
.SetBACName(rName
, eType
);
1093 // Insert origin document property
1094 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
= pDocShell
->getDocProperties()->getUserDefinedProperties();
1095 sfx::ClassificationKeyCreator
aCreator(SfxClassificationHelper::getPolicyType());
1096 svx::classification::insertCreationOrigin(xPropertyContainer
, aCreator
, sfx::ClassificationCreationOrigin::BAF_POLICY
);
1098 bool bHeaderIsNeeded
= aHelper
.HasDocumentHeader();
1099 bool bFooterIsNeeded
= aHelper
.HasDocumentFooter();
1100 OUString aWatermark
= aHelper
.GetDocumentWatermark();
1101 bool bWatermarkIsNeeded
= !aWatermark
.isEmpty();
1103 if (!bHeaderIsNeeded
&& !bFooterIsNeeded
&& !bWatermarkIsNeeded
&& !bHadWatermark
)
1106 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1107 uno::Reference
<container::XNameAccess
> xStyleFamilies
= xModel
->getStyleFamilies();
1108 uno::Reference
<container::XNameAccess
> xStyleFamily(xStyleFamilies
->getByName(u
"PageStyles"_ustr
), uno::UNO_QUERY
);
1109 const uno::Sequence
<OUString
> aStyles
= xStyleFamily
->getElementNames();
1111 for (const OUString
& rPageStyleName
: aStyles
)
1113 uno::Reference
<beans::XPropertySet
> xPageStyle(xStyleFamily
->getByName(rPageStyleName
), uno::UNO_QUERY
);
1114 uno::Reference
<lang::XMultiServiceFactory
> xMultiServiceFactory(xModel
);
1116 if (bHeaderIsNeeded
|| bWatermarkIsNeeded
|| bHadWatermark
)
1118 // If the header is off, turn it on.
1119 bool bHeaderIsOn
= false;
1120 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
1122 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_IS_ON
, uno::Any(true));
1124 // If the header already contains a document header field, no need to do anything.
1125 uno::Reference
<text::XText
> xHeaderText
;
1126 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
1128 if (bHeaderIsNeeded
)
1130 if (!lcl_hasField(xHeaderText
, DocInfoServiceName
, Concat2View(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER())))
1132 // Append a field to the end of the header text.
1133 uno::Reference
<beans::XPropertySet
> xField(xMultiServiceFactory
->createInstance(DocInfoServiceName
), uno::UNO_QUERY
);
1134 xField
->setPropertyValue(UNO_NAME_NAME
, uno::Any(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()));
1135 uno::Reference
<text::XTextContent
> xTextContent(xField
, uno::UNO_QUERY
);
1136 xHeaderText
->insertTextContent(xHeaderText
->getEnd(), xTextContent
, /*bAbsorb=*/false);
1140 SfxWatermarkItem aWatermarkItem
;
1141 aWatermarkItem
.SetText(aWatermark
);
1142 SetWatermark(aWatermarkItem
);
1145 if (bFooterIsNeeded
)
1147 // If the footer is off, turn it on.
1148 bool bFooterIsOn
= false;
1149 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_IS_ON
) >>= bFooterIsOn
;
1151 xPageStyle
->setPropertyValue(UNO_NAME_FOOTER_IS_ON
, uno::Any(true));
1153 // If the footer already contains a document header field, no need to do anything.
1154 uno::Reference
<text::XText
> xFooterText
;
1155 xPageStyle
->getPropertyValue(UNO_NAME_FOOTER_TEXT
) >>= xFooterText
;
1156 static OUString sFooter
= SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCFOOTER();
1157 if (!lcl_hasField(xFooterText
, DocInfoServiceName
, sFooter
))
1159 // Append a field to the end of the footer text.
1160 uno::Reference
<beans::XPropertySet
> xField(xMultiServiceFactory
->createInstance(DocInfoServiceName
), uno::UNO_QUERY
);
1161 xField
->setPropertyValue(UNO_NAME_NAME
, uno::Any(sFooter
));
1162 uno::Reference
<text::XTextContent
> xTextContent(xField
, uno::UNO_QUERY
);
1163 xFooterText
->insertTextContent(xFooterText
->getEnd(), xTextContent
, /*bAbsorb=*/false);
1169 // We pass xParent and xNodeSubject even though they point to the same thing because the UNO_QUERY is
1170 // on a performance-sensitive path.
1171 static void lcl_ApplyParagraphClassification(SwDoc
* pDoc
,
1172 const rtl::Reference
<SwXTextDocument
>& xModel
,
1173 const rtl::Reference
<SwXParagraph
>& xParent
,
1174 const css::uno::Reference
<css::rdf::XResource
>& xNodeSubject
,
1175 std::vector
<svx::ClassificationResult
> aResults
)
1177 if (!xNodeSubject
.is())
1180 // Remove all paragraph classification fields.
1183 uno::Reference
<text::XTextField
> xTextField
= lcl_FindParagraphClassificationField(xModel
, xParent
);
1184 if (!xTextField
.is())
1186 lcl_RemoveParagraphMetadataField(xTextField
);
1189 if (aResults
.empty())
1192 // Since we always insert at the start of the paragraph,
1193 // need to insert in reverse order.
1194 std::reverse(aResults
.begin(), aResults
.end());
1195 // Ignore "PARAGRAPH" types
1196 std::erase_if(aResults
,
1197 [](const svx::ClassificationResult
& rResult
)-> bool
1198 { return rResult
.meType
== svx::ClassificationType::PARAGRAPH
; });
1200 sfx::ClassificationKeyCreator
aKeyCreator(SfxClassificationHelper::getPolicyType());
1201 std::vector
<OUString
> aFieldNames
;
1202 for (size_t nIndex
= 0; nIndex
< aResults
.size(); ++nIndex
)
1204 const svx::ClassificationResult
& rResult
= aResults
[nIndex
];
1206 const bool isLast
= nIndex
== 0;
1207 const bool isFirst
= (nIndex
== aResults
.size() - 1);
1209 OUString sValue
= rResult
.msName
;
1210 switch (rResult
.meType
)
1212 case svx::ClassificationType::TEXT
:
1214 sKey
= aKeyCreator
.makeNumberedTextKey();
1218 case svx::ClassificationType::CATEGORY
:
1220 if (rResult
.msIdentifier
.isEmpty())
1222 sKey
= aKeyCreator
.makeCategoryNameKey();
1226 sValue
= rResult
.msIdentifier
;
1227 sKey
= aKeyCreator
.makeCategoryIdentifierKey();
1229 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xNodeSubject
, ParagraphClassificationAbbrRDFName
, rResult
.msAbbreviatedName
);
1233 case svx::ClassificationType::MARKING
:
1235 sKey
= aKeyCreator
.makeNumberedMarkingKey();
1239 case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
:
1241 sKey
= aKeyCreator
.makeNumberedIntellectualPropertyPartKey();
1249 OUString sDisplayText
= (isFirst
? ("(" + rResult
.msAbbreviatedName
) : rResult
.msAbbreviatedName
);
1251 sDisplayText
+= ")";
1252 lcl_UpdateParagraphClassificationField(pDoc
, xModel
, xParent
, sKey
, sValue
, sDisplayText
);
1253 aFieldNames
.emplace_back(sKey
);
1256 // Correct the order
1257 std::reverse(aFieldNames
.begin(), aFieldNames
.end());
1258 OUStringBuffer sFieldNames
;
1260 for (const OUString
& rFieldName
: aFieldNames
)
1263 sFieldNames
.append("/");
1264 sFieldNames
.append(rFieldName
);
1268 const OUString sOldFieldNames
= lcl_getRDF(xModel
, xNodeSubject
, ParagraphClassificationFieldNamesRDFName
).second
;
1269 SwRDFHelper::removeStatement(xModel
, MetaNS
, xNodeSubject
, ParagraphClassificationFieldNamesRDFName
, sOldFieldNames
);
1270 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xNodeSubject
, ParagraphClassificationFieldNamesRDFName
, sFieldNames
.makeStringAndClear());
1273 void SwEditShell::ApplyParagraphClassification(std::vector
<svx::ClassificationResult
> aResults
)
1275 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1276 if (!pDocShell
|| !GetCursor() || !GetCursor()->Start())
1279 SwTextNode
* pNode
= GetCursor()->Start()->GetNode().GetTextNode();
1280 if (pNode
== nullptr)
1283 // Prevent recursive validation since this is triggered on node updates, which we do below.
1284 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
1285 comphelper::ScopeGuard
const g([this, bOldValidationFlag
]() {
1286 SetParagraphSignatureValidation(bOldValidationFlag
);
1289 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1290 rtl::Reference
<SwXParagraph
> xParent
= SwXParagraph::CreateXParagraph(pNode
->GetDoc(), pNode
, nullptr);
1291 lcl_ApplyParagraphClassification(GetDoc(), xModel
, xParent
, css::uno::Reference
<css::rdf::XResource
>(xParent
), std::move(aResults
));
1294 static std::vector
<svx::ClassificationResult
> lcl_CollectParagraphClassification(const rtl::Reference
<SwXTextDocument
>& xModel
, const uno::Reference
<text::XTextContent
>& xParagraph
)
1296 std::vector
<svx::ClassificationResult
> aResult
;
1298 uno::Reference
<container::XEnumerationAccess
> xTextPortionEnumerationAccess(xParagraph
, uno::UNO_QUERY
);
1299 if (!xTextPortionEnumerationAccess
.is())
1302 uno::Reference
<container::XEnumeration
> xTextPortions
= xTextPortionEnumerationAccess
->createEnumeration();
1304 const sfx::ClassificationKeyCreator
aKeyCreator(SfxClassificationHelper::getPolicyType());
1306 while (xTextPortions
->hasMoreElements())
1308 uno::Reference
<beans::XPropertySet
> xTextPortion(xTextPortions
->nextElement(), uno::UNO_QUERY
);
1309 OUString aTextPortionType
;
1310 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE
) >>= aTextPortionType
;
1311 if (aTextPortionType
!= UNO_NAME_TEXT_FIELD
)
1314 uno::Reference
<lang::XServiceInfo
> xField
;
1315 xTextPortion
->getPropertyValue(UNO_NAME_TEXT_FIELD
) >>= xField
;
1316 if (!xField
->supportsService(MetadataFieldServiceName
))
1319 uno::Reference
<text::XTextField
> xTextField(xField
, uno::UNO_QUERY
);
1320 const OUString sPolicy
= SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
1321 const std::pair
<OUString
, OUString
> rdfNamePair
= lcl_getFieldRDFByPrefix(xModel
, xTextField
, sPolicy
);
1323 uno::Reference
<text::XTextRange
> xTextRange(xField
, uno::UNO_QUERY
);
1324 const OUString aName
= rdfNamePair
.first
;
1325 const OUString aValue
= rdfNamePair
.second
;
1326 static constexpr OUString
sBlank(u
""_ustr
);
1327 if (aKeyCreator
.isMarkingTextKey(aName
))
1329 aResult
.emplace_back(svx::ClassificationType::TEXT
, aValue
, sBlank
, sBlank
);
1331 else if (aKeyCreator
.isCategoryNameKey(aName
))
1333 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, aValue
, sBlank
, sBlank
);
1335 else if (aKeyCreator
.isCategoryIdentifierKey(aName
))
1337 aResult
.emplace_back(svx::ClassificationType::CATEGORY
, sBlank
, sBlank
, aValue
);
1339 else if (aKeyCreator
.isMarkingKey(aName
))
1341 aResult
.emplace_back(svx::ClassificationType::MARKING
, aValue
, sBlank
, sBlank
);
1343 else if (aKeyCreator
.isIntellectualPropertyPartKey(aName
))
1345 aResult
.emplace_back(svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
, xTextRange
->getString(), sBlank
, sBlank
);
1352 std::vector
<svx::ClassificationResult
> SwEditShell::CollectParagraphClassification()
1354 std::vector
<svx::ClassificationResult
> aResult
;
1356 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1357 if (!pDocShell
|| !GetCursor() || !GetCursor()->Start())
1360 SwTextNode
* pNode
= GetCursor()->Start()->GetNode().GetTextNode();
1361 if (pNode
== nullptr)
1364 rtl::Reference
<SwXParagraph
> xParent
= SwXParagraph::CreateXParagraph(pNode
->GetDoc(), pNode
, nullptr);
1365 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1366 return lcl_CollectParagraphClassification(xModel
, xParent
);
1369 static sal_Int16
lcl_GetAngle(const drawing::HomogenMatrix3
& rMatrix
)
1371 basegfx::B2DHomMatrix aTransformation
;
1372 basegfx::B2DTuple aScale
;
1373 basegfx::B2DTuple aTranslate
;
1377 aTransformation
.set(0, 0, rMatrix
.Line1
.Column1
);
1378 aTransformation
.set(0, 1, rMatrix
.Line1
.Column2
);
1379 aTransformation
.set(0, 2, rMatrix
.Line1
.Column3
);
1380 aTransformation
.set(1, 0, rMatrix
.Line2
.Column1
);
1381 aTransformation
.set(1, 1, rMatrix
.Line2
.Column2
);
1382 aTransformation
.set(1, 2, rMatrix
.Line2
.Column3
);
1383 // For this to be a valid 2D transform matrix, the last row must be [0,0,1]
1384 assert( rMatrix
.Line3
.Column1
== 0 );
1385 assert( rMatrix
.Line3
.Column2
== 0 );
1386 assert( rMatrix
.Line3
.Column3
== 1 );
1388 aTransformation
.decompose(aScale
, aTranslate
, fRotate
, fShear
);
1389 sal_Int16 nDeg
= round(basegfx::rad2deg(fRotate
));
1390 return nDeg
< 0 ? round(nDeg
) * -1 : round(360.0 - nDeg
);
1393 SfxWatermarkItem
SwEditShell::GetWatermark() const
1395 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1397 return SfxWatermarkItem();
1399 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1400 uno::Reference
<container::XNameAccess
> xStyleFamilies
= xModel
->getStyleFamilies();
1401 uno::Reference
<container::XNameAccess
> xStyleFamily(xStyleFamilies
->getByName(u
"PageStyles"_ustr
), 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
= u
""_ustr
;
1416 bool bSuccess
= false;
1417 uno::Reference
<drawing::XShape
> xWatermark
= lcl_getWatermark(xHeaderText
, u
"com.sun.star.drawing.CustomShape"_ustr
, 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(u
"Transformation"_ustr
) >>= 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 rtl::Reference
<SwXTextDocument
>& xModel
,
1448 const uno::Reference
<beans::XPropertySet
>& xPageStyle
,
1449 const uno::Reference
<text::XText
>& xHeaderText
)
1451 if (!xHeaderText
.is())
1454 OUString aShapeServiceName
= u
"com.sun.star.drawing.CustomShape"_ustr
;
1455 OUString sWatermark
= WATERMARK_NAME
;
1456 bool bSuccess
= false;
1457 uno::Reference
<drawing::XShape
> xWatermark
= lcl_getWatermark(xHeaderText
, aShapeServiceName
, sWatermark
, bSuccess
);
1459 bool bDeleteWatermark
= rWatermark
.GetText().isEmpty();
1460 if (xWatermark
.is())
1462 drawing::HomogenMatrix3 aMatrix
;
1463 Color nColor
= 0xc0c0c0;
1464 sal_Int16 nTransparency
= 50;
1465 sal_Int16 nAngle
= 45;
1466 OUString aFont
= u
""_ustr
;
1468 uno::Reference
<beans::XPropertySet
> xPropertySet(xWatermark
, uno::UNO_QUERY
);
1469 xPropertySet
->getPropertyValue(UNO_NAME_CHAR_FONT_NAME
) >>= aFont
;
1470 xPropertySet
->getPropertyValue(UNO_NAME_FILLCOLOR
) >>= nColor
;
1471 xPropertySet
->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE
) >>= nTransparency
;
1472 xPropertySet
->getPropertyValue(u
"Transformation"_ustr
) >>= aMatrix
;
1473 nAngle
= lcl_GetAngle(aMatrix
);
1475 // If the header already contains a watermark, see if it its text is up to date.
1476 uno::Reference
<text::XTextRange
> xTextRange(xWatermark
, uno::UNO_QUERY
);
1477 if (xTextRange
->getString() != rWatermark
.GetText()
1478 || aFont
!= rWatermark
.GetFont()
1479 || nColor
!= rWatermark
.GetColor()
1480 || nAngle
!= rWatermark
.GetAngle()
1481 || nTransparency
!= rWatermark
.GetTransparency()
1482 || bDeleteWatermark
)
1484 // No: delete it and we'll insert a replacement.
1485 uno::Reference
<lang::XComponent
> xComponent(xWatermark
, uno::UNO_QUERY
);
1486 xComponent
->dispose();
1491 if (!bSuccess
|| xWatermark
.is() || bDeleteWatermark
)
1494 const OUString
& sFont
= rWatermark
.GetFont();
1495 sal_Int16 nAngle
= rWatermark
.GetAngle();
1496 sal_Int16 nTransparency
= rWatermark
.GetTransparency();
1497 Color nColor
= rWatermark
.GetColor();
1502 ScopedVclPtrInstance
<VirtualDevice
> pDevice
;
1503 vcl::Font aFont
= pDevice
->GetFont();
1504 aFont
.SetFamilyName(sFont
);
1505 aFont
.SetFontSize(Size(0, 96));
1506 pDevice
->SetFont(aFont
);
1508 auto nTextWidth
= pDevice
->GetTextWidth(rWatermark
.GetText());
1511 fRatio
= pDevice
->GetTextHeight();
1512 fRatio
/= nTextWidth
;
1516 sal_Int32 nWidth
= 0;
1518 xPageStyle
->getPropertyValue(UNO_NAME_SIZE
) >>= aSize
;
1519 if (aSize
.Width
< aSize
.Height
)
1522 sal_Int32 nLeftMargin
= 0;
1523 xPageStyle
->getPropertyValue(UNO_NAME_LEFT_MARGIN
) >>= nLeftMargin
;
1524 sal_Int32 nRightMargin
= 0;
1525 xPageStyle
->getPropertyValue(UNO_NAME_RIGHT_MARGIN
) >>= nRightMargin
;
1526 nWidth
= aSize
.Width
- nLeftMargin
- nRightMargin
;
1531 sal_Int32 nTopMargin
= 0;
1532 xPageStyle
->getPropertyValue(UNO_NAME_TOP_MARGIN
) >>= nTopMargin
;
1533 sal_Int32 nBottomMargin
= 0;
1534 xPageStyle
->getPropertyValue(UNO_NAME_BOTTOM_MARGIN
) >>= nBottomMargin
;
1535 nWidth
= aSize
.Height
- nTopMargin
- nBottomMargin
;
1537 sal_Int32 nHeight
= fRatio
* nWidth
;
1539 // Create and insert the shape.
1540 uno::Reference
<drawing::XShape
> xShape(xModel
->createInstance(aShapeServiceName
), uno::UNO_QUERY
);
1542 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
1543 xNamed
->setName(sWatermark
);
1545 basegfx::B2DHomMatrix aTransformation
;
1546 aTransformation
.identity();
1547 aTransformation
.scale(nWidth
, nHeight
);
1548 aTransformation
.rotate(-basegfx::deg2rad(nAngle
));
1549 drawing::HomogenMatrix3 aMatrix
;
1550 aMatrix
.Line1
.Column1
= aTransformation
.get(0, 0);
1551 aMatrix
.Line1
.Column2
= aTransformation
.get(0, 1);
1552 aMatrix
.Line1
.Column3
= aTransformation
.get(0, 2);
1553 aMatrix
.Line2
.Column1
= aTransformation
.get(1, 0);
1554 aMatrix
.Line2
.Column2
= aTransformation
.get(1, 1);
1555 aMatrix
.Line2
.Column3
= aTransformation
.get(1, 2);
1556 aMatrix
.Line3
.Column1
= 0;
1557 aMatrix
.Line3
.Column2
= 0;
1558 aMatrix
.Line3
.Column3
= 1;
1559 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
1560 xPropertySet
->setPropertyValue(UNO_NAME_ANCHOR_TYPE
, uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1561 uno::Reference
<text::XTextContent
> xTextContent(xShape
, uno::UNO_QUERY
);
1562 xHeaderText
->insertTextContent(xHeaderText
->getEnd(), xTextContent
, false);
1564 // The remaining properties have to be set after the shape is inserted: do that in one batch to avoid flickering.
1565 uno::Reference
<document::XActionLockable
> xLockable(xShape
, uno::UNO_QUERY
);
1566 xLockable
->addActionLock();
1567 xPropertySet
->setPropertyValue(UNO_NAME_FILLCOLOR
, uno::Any(static_cast<sal_Int32
>(nColor
)));
1568 xPropertySet
->setPropertyValue(UNO_NAME_FILLSTYLE
, uno::Any(drawing::FillStyle_SOLID
));
1569 xPropertySet
->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE
, uno::Any(nTransparency
));
1570 xPropertySet
->setPropertyValue(UNO_NAME_LINESTYLE
, uno::Any(drawing::LineStyle_NONE
));
1571 xPropertySet
->setPropertyValue(UNO_NAME_OPAQUE
, uno::Any(false));
1572 xPropertySet
->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT
, uno::Any(false));
1573 xPropertySet
->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH
, uno::Any(false));
1574 xPropertySet
->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT
, uno::Any(nHeight
));
1575 xPropertySet
->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH
, uno::Any(nWidth
));
1576 xPropertySet
->setPropertyValue(UNO_NAME_TEXT_WRAP
, uno::Any(text::WrapTextMode_THROUGH
));
1577 xPropertySet
->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION
, uno::Any(text::RelOrientation::PAGE_PRINT_AREA
));
1578 xPropertySet
->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION
, uno::Any(text::RelOrientation::PAGE_PRINT_AREA
));
1579 xPropertySet
->setPropertyValue(UNO_NAME_CHAR_FONT_NAME
, uno::Any(sFont
));
1580 xPropertySet
->setPropertyValue(UNO_NAME_CHAR_FONT_NAME_ASIAN
, uno::Any(sFont
));
1581 xPropertySet
->setPropertyValue(UNO_NAME_CHAR_FONT_NAME_COMPLEX
, uno::Any(sFont
));
1582 xPropertySet
->setPropertyValue(UNO_NAME_CHAR_HEIGHT
, uno::Any(WATERMARK_AUTO_SIZE
));
1583 xPropertySet
->setPropertyValue(u
"Transformation"_ustr
, uno::Any(aMatrix
));
1585 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
1586 xTextRange
->setString(rWatermark
.GetText());
1588 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
1589 xDefaulter
->createCustomShapeDefaults(u
"fontwork-plain-text"_ustr
);
1591 auto aGeomPropSeq
= xPropertySet
->getPropertyValue(u
"CustomShapeGeometry"_ustr
).get
< uno::Sequence
<beans::PropertyValue
> >();
1592 auto aGeomPropVec
= comphelper::sequenceToContainer
< std::vector
<beans::PropertyValue
> >(aGeomPropSeq
);
1593 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence(
1595 {"TextPath", uno::Any(true)},
1597 auto it
= std::find_if(aGeomPropVec
.begin(), aGeomPropVec
.end(), [](const beans::PropertyValue
& rValue
)
1599 return rValue
.Name
== "TextPath";
1601 if (it
== aGeomPropVec
.end())
1602 aGeomPropVec
.push_back(comphelper::makePropertyValue(u
"TextPath"_ustr
, aPropertyValues
));
1604 it
->Value
<<= aPropertyValues
;
1605 xPropertySet
->setPropertyValue(u
"CustomShapeGeometry"_ustr
, uno::Any(comphelper::containerToSequence(aGeomPropVec
)));
1607 // tdf#108494, tdf#109313 the header height was switched to height of a watermark
1608 // and shape was moved to the lower part of a page, force position update
1609 xPropertySet
->getPropertyValue(u
"Transformation"_ustr
) >>= aMatrix
;
1610 xPropertySet
->setPropertyValue(u
"Transformation"_ustr
, uno::Any(aMatrix
));
1612 xPropertySet
->setPropertyValue(UNO_NAME_HORI_ORIENT
, uno::Any(text::HoriOrientation::CENTER
));
1613 xPropertySet
->setPropertyValue(UNO_NAME_VERT_ORIENT
, uno::Any(text::VertOrientation::CENTER
));
1615 xLockable
->removeActionLock();
1618 void SwEditShell::SetWatermark(const SfxWatermarkItem
& rWatermark
)
1620 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1623 const bool bNoWatermark
= rWatermark
.GetText().isEmpty();
1625 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1626 uno::Reference
<container::XNameAccess
> xStyleFamilies
= xModel
->getStyleFamilies();
1627 uno::Reference
<container::XNameAccess
> xStyleFamily(xStyleFamilies
->getByName(u
"PageStyles"_ustr
), uno::UNO_QUERY
);
1628 const uno::Sequence
<OUString
> aStyles
= xStyleFamily
->getElementNames();
1630 for (const OUString
& rPageStyleName
: aStyles
)
1632 uno::Reference
<beans::XPropertySet
> xPageStyle(xStyleFamily
->getByName(rPageStyleName
), uno::UNO_QUERY
);
1634 // If the header is off, turn it on.
1635 bool bHeaderIsOn
= false;
1636 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_ON
) >>= bHeaderIsOn
;
1640 continue; // the style doesn't have any watermark - no need to do anything
1642 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_IS_ON
, uno::Any(true));
1645 // backup header height
1646 bool bDynamicHeight
= true;
1647 sal_Int32 nOldValue
;
1648 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_HEIGHT
) >>= nOldValue
;
1649 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT
) >>= bDynamicHeight
;
1650 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT
, uno::Any(false));
1652 // If the header already contains a document header field, no need to do anything.
1653 uno::Reference
<text::XText
> xHeaderText
;
1654 uno::Reference
<text::XText
> xHeaderTextFirst
;
1655 uno::Reference
<text::XText
> xHeaderTextLeft
;
1656 uno::Reference
<text::XText
> xHeaderTextRight
;
1658 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT
) >>= xHeaderText
;
1659 lcl_placeWatermarkInHeader(rWatermark
, xModel
, xPageStyle
, xHeaderText
);
1661 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT_FIRST
) >>= xHeaderTextFirst
;
1662 lcl_placeWatermarkInHeader(rWatermark
, xModel
, xPageStyle
, xHeaderTextFirst
);
1664 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT_LEFT
) >>= xHeaderTextLeft
;
1665 lcl_placeWatermarkInHeader(rWatermark
, xModel
, xPageStyle
, xHeaderTextLeft
);
1667 xPageStyle
->getPropertyValue(UNO_NAME_HEADER_TEXT_RIGHT
) >>= xHeaderTextRight
;
1668 lcl_placeWatermarkInHeader(rWatermark
, xModel
, xPageStyle
, xHeaderTextRight
);
1670 // tdf#108494 the header height was switched to height of a watermark
1671 // and shape was moved to the lower part of a page
1672 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_HEIGHT
, uno::Any(sal_Int32(11)));
1673 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_HEIGHT
, uno::Any(nOldValue
));
1674 xPageStyle
->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT
, uno::Any(bDynamicHeight
));
1678 SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc
& rDoc
,
1679 uno::Reference
<text::XTextField
> xField
,
1680 uno::Reference
<text::XTextContent
> xParent
,
1682 : SwUndo(SwUndoId::PARA_SIGN_ADD
, &rDoc
),
1684 m_xField(std::move(xField
)),
1685 m_xParent(std::move(xParent
)),
1688 // Save the metadata and field content to undo/redo.
1689 if (SwDocShell
* pShell
= m_rDoc
.GetDocShell())
1691 rtl::Reference
<SwXTextDocument
> xModel
= pShell
->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();
1706 void SwUndoParagraphSigning::UndoImpl(::sw::UndoRedoContext
&)
1714 void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext
&)
1722 void SwUndoParagraphSigning::RepeatImpl(::sw::RepeatContext
&)
1726 void SwUndoParagraphSigning::Insert()
1728 // Disable undo to avoid introducing noise when we edit the metadata field.
1729 const bool isUndoEnabled
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
1730 m_rDoc
.GetIDocumentUndoRedo().DoUndo(false);
1732 // Prevent validation since this will trigger a premature validation
1733 // upon inserting, but before setting the metadata.
1734 SwEditShell
* pEditSh
= m_rDoc
.GetEditShell();
1735 const bool bOldValidationFlag
= pEditSh
&& pEditSh
->SetParagraphSignatureValidation(false);
1736 comphelper::ScopeGuard
const g([&] () {
1738 pEditSh
->SetParagraphSignatureValidation(bOldValidationFlag
);
1739 m_rDoc
.GetIDocumentUndoRedo().DoUndo(isUndoEnabled
);
1742 if (SwDocShell
* pShell
= m_rDoc
.GetDocShell())
1744 m_xField
= lcl_InsertParagraphSignature(pShell
->GetBaseModel(), m_xParent
, m_signature
, m_usage
);
1745 lcl_DoUpdateParagraphSignatureField(m_rDoc
, m_xField
, m_display
);
1749 void SwUndoParagraphSigning::Remove()
1751 // Disable undo to avoid introducing noise when we edit the metadata field.
1752 const bool isUndoEnabled
= m_rDoc
.GetIDocumentUndoRedo().DoesUndo();
1753 m_rDoc
.GetIDocumentUndoRedo().DoUndo(false);
1755 // Prevent validation since this will trigger a premature validation
1757 SwEditShell
* pEditSh
= m_rDoc
.GetEditShell();
1758 const bool bOldValidationFlag
= pEditSh
&& pEditSh
->SetParagraphSignatureValidation(false);
1759 comphelper::ScopeGuard
const g([&] () {
1761 pEditSh
->SetParagraphSignatureValidation(bOldValidationFlag
);
1762 m_rDoc
.GetIDocumentUndoRedo().DoUndo(isUndoEnabled
);
1765 lcl_RemoveParagraphMetadataField(m_xField
);
1768 void SwEditShell::SignParagraph()
1770 SwDoc
& rDoc
= *GetDoc();
1771 SwDocShell
* pDocShell
= rDoc
.GetDocShell();
1772 if (!pDocShell
|| !GetCursor() || !GetCursor()->Start())
1774 const SwPosition
* pPosStart
= GetCursor()->Start();
1777 SwTextNode
* pNode
= pPosStart
->GetNode().GetTextNode();
1781 // Table text signing is not supported.
1782 if (pNode
->FindTableNode() != nullptr)
1785 // 1. Get the text (without fields).
1786 const rtl::Reference
<SwXParagraph
> xParagraph
= SwXParagraph::CreateXParagraph(pNode
->GetDoc(), pNode
, nullptr);
1787 const OString utf8Text
= lcl_getParagraphBodyText(xParagraph
);
1788 if (utf8Text
.isEmpty())
1791 // 2. Get certificate.
1792 uno::Reference
<security::XDocumentDigitalSignatures
> xSigner(
1793 // here none of the version-dependent methods are called
1794 security::DocumentDigitalSignatures::createDefault(
1795 comphelper::getProcessComponentContext()));
1797 uno::Sequence
<css::beans::PropertyValue
> aProperties
;
1798 uno::Reference
<security::XCertificate
> xCertificate
= xSigner
->chooseCertificateWithProps(aProperties
);
1799 if (!xCertificate
.is())
1803 svl::crypto::SigningContext aSigningContext
;
1804 aSigningContext
.m_xCertificate
= std::move(xCertificate
);
1805 svl::crypto::Signing
signing(aSigningContext
);
1806 signing
.AddDataRange(utf8Text
.getStr(), utf8Text
.getLength());
1807 OStringBuffer sigBuf
;
1808 if (!signing
.Sign(sigBuf
))
1811 const OUString signature
= OStringToOUString(sigBuf
, RTL_TEXTENCODING_UTF8
, 0);
1813 auto it
= std::find_if(aProperties
.begin(), aProperties
.end(), [](const beans::PropertyValue
& rValue
)
1815 return rValue
.Name
== "Usage";
1819 if (it
!= aProperties
.end())
1820 it
->Value
>>= aUsage
;
1823 // Prevent validation since this will trigger a premature validation
1824 // upon inserting, but before setting the metadata.
1825 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
1826 comphelper::ScopeGuard
const g([this, bOldValidationFlag
] () {
1827 SetParagraphSignatureValidation(bOldValidationFlag
);
1830 rDoc
.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD
, nullptr);
1832 const rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1833 uno::Reference
<css::text::XTextField
> xField
= lcl_InsertParagraphSignature(xModel
, xParagraph
, signature
, aUsage
);
1835 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel
, xParagraph
, xField
, utf8Text
);
1837 rDoc
.GetIDocumentUndoRedo().AppendUndo(
1838 std::make_unique
<SwUndoParagraphSigning
>(rDoc
, xField
, xParagraph
, true));
1840 rDoc
.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD
, nullptr);
1843 void SwEditShell::ValidateParagraphSignatures(SwTextNode
* pNode
, bool updateDontRemove
)
1845 if (!pNode
|| !IsParagraphSignatureValidationEnabled())
1848 // Table text signing is not supported.
1849 if (pNode
->FindTableNode() != nullptr)
1852 // Prevent recursive validation since this is triggered on node updates, which we do below.
1853 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
1854 comphelper::ScopeGuard
const g([this, bOldValidationFlag
] () {
1855 SetParagraphSignatureValidation(bOldValidationFlag
);
1858 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1862 uno::Sequence
<uno::Reference
<css::rdf::XURI
>> aGraphNames
= SwRDFHelper::getGraphNames(pDocShell
->GetBaseModel(), MetaNS
);
1863 rtl::Reference
<SwXParagraph
> xParentText
= SwXParagraph::CreateXParagraph(*GetDoc(), pNode
, nullptr);
1864 lcl_ValidateParagraphSignatures(*GetDoc(), xParentText
, updateDontRemove
, aGraphNames
);
1867 void SwEditShell::ValidateCurrentParagraphSignatures(bool updateDontRemove
)
1869 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1870 if (!pDocShell
|| !GetCursor() || !GetCursor()->Start() || !IsParagraphSignatureValidationEnabled())
1873 SwPaM
* pPaM
= GetCursor();
1874 const SwPosition
* pPosStart
= pPaM
->Start();
1875 SwTextNode
* pNode
= pPosStart
->GetNode().GetTextNode();
1876 ValidateParagraphSignatures(pNode
, updateDontRemove
);
1879 void SwEditShell::ValidateAllParagraphSignatures(bool updateDontRemove
)
1881 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1882 if (!pDocShell
|| !IsParagraphSignatureValidationEnabled())
1885 // Prevent recursive validation since this is triggered on node updates, which we do below.
1886 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
1887 comphelper::ScopeGuard
const g([this, bOldValidationFlag
] () {
1888 SetParagraphSignatureValidation(bOldValidationFlag
);
1891 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1892 uno::Reference
<text::XText
> xParent
= xModel
->getText();
1893 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumerationAccess(xParent
, uno::UNO_QUERY
);
1894 if (!xParagraphEnumerationAccess
.is())
1896 uno::Reference
<container::XEnumeration
> xParagraphs
= xParagraphEnumerationAccess
->createEnumeration();
1897 if (!xParagraphs
.is())
1899 uno::Sequence
<uno::Reference
<css::rdf::XURI
>> aGraphNames
= SwRDFHelper::getGraphNames(pDocShell
->GetBaseModel(), MetaNS
);
1900 while (xParagraphs
->hasMoreElements())
1902 uno::Reference
<text::XTextContent
> xParagraph(xParagraphs
->nextElement(), uno::UNO_QUERY
);
1903 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph
, updateDontRemove
, aGraphNames
);
1907 static uno::Reference
<text::XTextField
> lcl_GetParagraphMetadataFieldAtIndex(const SwDocShell
* pDocSh
, SwTextNode
const * pNode
, const sal_uLong index
)
1909 uno::Reference
<text::XTextField
> xTextField
;
1910 if (pNode
!= nullptr && pDocSh
!= nullptr)
1912 SwTextAttr
* pAttr
= pNode
->GetTextAttrAt(index
, RES_TXTATR_METAFIELD
);
1913 SwTextMeta
* pTextMeta
= static_txtattr_cast
<SwTextMeta
*>(pAttr
);
1914 if (pTextMeta
!= nullptr)
1916 SwFormatMeta
& rFormatMeta(static_cast<SwFormatMeta
&>(pTextMeta
->GetAttr()));
1917 if (::sw::Meta
* pMeta
= rFormatMeta
.GetMeta())
1919 const css::uno::Reference
<css::rdf::XResource
> xSubject
= pMeta
->MakeUnoObject();
1920 rtl::Reference
<SwXTextDocument
> xModel
= pDocSh
->GetBaseModel();
1921 const std::map
<OUString
, OUString
> aStatements
= lcl_getRDFStatements(xModel
, xSubject
);
1922 if (aStatements
.find(ParagraphSignatureIdRDFName
) != aStatements
.end() ||
1923 aStatements
.find(ParagraphClassificationNameRDFName
) != aStatements
.end())
1925 xTextField
= uno::Reference
<text::XTextField
>(xSubject
, uno::UNO_QUERY
);
1934 void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
1936 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
1937 if (!pDocShell
|| !IsParagraphSignatureValidationEnabled())
1940 // Prevent recursive validation since this is triggered on node updates, which we do below.
1941 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
1942 comphelper::ScopeGuard
const g([this, bOldValidationFlag
] () {
1943 SetParagraphSignatureValidation(bOldValidationFlag
);
1946 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
1947 rtl::Reference
<SwXBodyText
> xBodyText
= xModel
->getBodyText();
1948 if (!xBodyText
.is())
1950 rtl::Reference
<SwXParagraphEnumeration
> xParagraphs
= xBodyText
->createParagraphEnumeration();
1952 static constexpr OUString
sBlank(u
""_ustr
);
1953 const sfx::ClassificationKeyCreator
aKeyCreator(SfxClassificationHelper::getPolicyType());
1954 uno::Sequence
<uno::Reference
<css::rdf::XURI
>> aGraphNames
= SwRDFHelper::getGraphNames(xModel
, MetaNS
);
1955 while (xParagraphs
->hasMoreElements())
1957 uno::Reference
<text::XTextContent
> xParaOrTable(xParagraphs
->nextElement(), uno::UNO_QUERY
);
1958 rtl::Reference
<SwXParagraph
> xParagraph(dynamic_cast<SwXParagraph
*>(xParaOrTable
.get()));
1962 const css::uno::Reference
<css::rdf::XResource
> xSubject(xParagraph
);
1963 std::map
<OUString
, OUString
> aParagraphStatements
= SwRDFHelper::getStatements(xModel
, aGraphNames
, xSubject
);
1964 auto it
= aParagraphStatements
.find(ParagraphClassificationFieldNamesRDFName
);
1965 const OUString sFieldNames
= (it
!= aParagraphStatements
.end()) ? it
->second
: OUString();
1967 std::vector
<svx::ClassificationResult
> aResults
;
1968 if (!sFieldNames
.isEmpty())
1971 sal_Int32 nIndex
= 0;
1974 const OUString sCurFieldName
= sFieldNames
.getToken(0, '/', nIndex
);
1975 if (sCurFieldName
.isEmpty())
1980 it
= aParagraphStatements
.find(sCurFieldName
);
1981 if (it
!= aParagraphStatements
.end())
1984 sValue
= it
->second
;
1987 if (aKeyCreator
.isMarkingTextKey(sName
))
1989 aResults
.emplace_back(svx::ClassificationType::TEXT
, sValue
, sValue
, sBlank
);
1991 else if (aKeyCreator
.isCategoryNameKey(sName
))
1993 const std::pair
<OUString
, OUString
> pairAbbr
= lcl_getRDF(xModel
, xSubject
, ParagraphClassificationAbbrRDFName
);
1994 const OUString sAbbreviatedName
= (!pairAbbr
.second
.isEmpty() ? pairAbbr
.second
: sValue
);
1995 aResults
.emplace_back(svx::ClassificationType::CATEGORY
, sValue
, sAbbreviatedName
, sBlank
);
1997 else if (aKeyCreator
.isCategoryIdentifierKey(sName
))
1999 const std::pair
<OUString
, OUString
> pairAbbr
= lcl_getRDF(xModel
, xSubject
, ParagraphClassificationAbbrRDFName
);
2000 const OUString sAbbreviatedName
= (!pairAbbr
.second
.isEmpty() ? pairAbbr
.second
: sValue
);
2001 aResults
.emplace_back(svx::ClassificationType::CATEGORY
, sBlank
, sAbbreviatedName
, sValue
);
2003 else if (aKeyCreator
.isMarkingKey(sName
))
2005 aResults
.emplace_back(svx::ClassificationType::MARKING
, sValue
, sValue
, sBlank
);
2007 else if (aKeyCreator
.isIntellectualPropertyPartKey(sName
))
2009 aResults
.emplace_back(svx::ClassificationType::INTELLECTUAL_PROPERTY_PART
, sValue
, sValue
, sBlank
);
2012 while (nIndex
>= 0);
2015 // Update classification based on results.
2016 lcl_ApplyParagraphClassification(GetDoc(), xModel
, xParagraph
, xSubject
, std::move(aResults
));
2019 std::map
<OUString
, SignatureDescr
> aSignatures
;
2020 for (const auto& pair
: aParagraphStatements
)
2022 const OUString
& sName
= pair
.first
;
2023 if (sName
.startsWith(ParagraphSignatureRDFNamespace
))
2025 const OUString sSuffix
= sName
.copy(ParagraphSignatureRDFNamespace
.getLength());
2026 const sal_Int32 index
= sSuffix
.indexOf(":");
2029 const OUString id
= sSuffix
.copy(0, index
);
2030 const OUString type
= sSuffix
.copy(index
);
2031 const OUString
& sValue
= pair
.second
;
2032 if (type
== ParagraphSignatureDateRDFName
)
2033 aSignatures
[id
].msDate
= sValue
;
2034 else if (type
== ParagraphSignatureUsageRDFName
)
2035 aSignatures
[id
].msUsage
= sValue
;
2036 else if (type
== ParagraphSignatureDigestRDFName
)
2037 aSignatures
[id
].msSignature
= sValue
;
2042 for (const auto& pair
: aSignatures
)
2044 uno::Reference
<text::XTextField
> xField
= lcl_findFieldByRDF(xModel
, xParagraph
, ParagraphSignatureIdRDFName
, pair
.first
);
2047 xField
= uno::Reference
<text::XTextField
>(xModel
->createInstance(MetadataFieldServiceName
), uno::UNO_QUERY
);
2049 // Add the signature at the end.
2050 xField
->attach(xParagraph
->getAnchor()->getEnd());
2052 const css::uno::Reference
<css::rdf::XResource
> xFieldSubject(xField
, uno::UNO_QUERY
);
2053 SwRDFHelper::addStatement(xModel
, MetaNS
, MetaFilename
, xFieldSubject
, ParagraphSignatureIdRDFName
, pair
.first
);
2055 const OString utf8Text
= lcl_getParagraphBodyText(xParagraph
);
2056 lcl_UpdateParagraphSignatureField(*GetDoc(), xModel
, xParagraph
, xField
, utf8Text
);
2060 lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph
, true, aGraphNames
); // Validate and Update signatures.
2062 catch (const std::exception
&)
2068 bool SwEditShell::IsCursorInParagraphMetadataField() const
2070 if (GetCursor() && GetCursor()->Start())
2072 SwTextNode
* pNode
= GetCursor()->Start()->GetNode().GetTextNode();
2073 const sal_uLong index
= GetCursor()->Start()->GetContentIndex();
2074 uno::Reference
<text::XTextField
> xField
= lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode
, index
);
2081 bool SwEditShell::RemoveParagraphMetadataFieldAtCursor()
2083 if (GetCursor() && GetCursor()->Start())
2085 SwTextNode
* pNode
= GetCursor()->Start()->GetNode().GetTextNode();
2086 sal_uLong index
= GetCursor()->Start()->GetContentIndex();
2087 uno::Reference
<text::XTextField
> xField
= lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode
, index
);
2090 // Try moving the cursor to see if we're _facing_ a metafield or not,
2091 // as opposed to being within one.
2092 index
--; // Backspace moves left
2094 xField
= lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode
, index
);
2099 lcl_RemoveParagraphMetadataField(xField
);
2107 static OUString
lcl_GetParagraphClassification(SfxClassificationHelper
& rHelper
, sfx::ClassificationKeyCreator
const & rKeyCreator
,
2108 const rtl::Reference
<SwXTextDocument
>& xModel
, const rtl::Reference
<SwXParagraph
>& xParagraph
)
2110 uno::Reference
<text::XTextField
> xTextField
;
2111 xTextField
= lcl_FindParagraphClassificationField(xModel
, xParagraph
, rKeyCreator
.makeCategoryIdentifierKey());
2112 if (xTextField
.is())
2114 const std::pair
<OUString
, OUString
> rdfValuePair
= lcl_getRDF(xModel
, xTextField
, ParagraphClassificationValueRDFName
);
2115 return rHelper
.GetBACNameForIdentifier(rdfValuePair
.second
);
2118 xTextField
= lcl_FindParagraphClassificationField(xModel
, xParagraph
, rKeyCreator
.makeCategoryNameKey());
2119 if (xTextField
.is())
2121 return lcl_getRDF(xModel
, xTextField
, ParagraphClassificationNameRDFName
).second
;
2127 static OUString
lcl_GetHighestClassificationParagraphClass(SwPaM
* pCursor
)
2129 OUString sHighestClass
;
2131 SwTextNode
* pNode
= pCursor
->Start()->GetNode().GetTextNode();
2132 if (pNode
== nullptr)
2133 return sHighestClass
;
2135 SwDocShell
* pDocShell
= pNode
->GetDoc().GetDocShell();
2137 return sHighestClass
;
2139 SfxClassificationHelper
aHelper(pDocShell
->getDocProperties());
2140 sfx::ClassificationKeyCreator
aKeyCreator(SfxClassificationHelper::getPolicyType());
2142 rtl::Reference
<SwXTextDocument
> xModel
= pDocShell
->GetBaseModel();
2143 rtl::Reference
<SwXBodyText
> xBodyText
= xModel
->getBodyText();
2145 rtl::Reference
<SwXParagraphEnumeration
> xParagraphs
= xBodyText
->createParagraphEnumeration();
2146 while (xParagraphs
->hasMoreElements())
2148 uno::Reference
<text::XTextContent
> xParaOrTable(xParagraphs
->nextElement(), uno::UNO_QUERY
);
2149 rtl::Reference
<SwXParagraph
> xParagraph(dynamic_cast<SwXParagraph
*>(xParaOrTable
.get()));
2150 const OUString sCurrentClass
= lcl_GetParagraphClassification(aHelper
, aKeyCreator
, xModel
, xParagraph
);
2151 sHighestClass
= aHelper
.GetHigherClass(sHighestClass
, sCurrentClass
);
2154 return sHighestClass
;
2157 void SwEditShell::ClassifyDocPerHighestParagraphClass()
2159 SwDocShell
* pDocShell
= GetDoc()->GetDocShell();
2163 // Bail out as early as possible if we don't have paragraph classification.
2164 if (!SwRDFHelper::hasMetadataGraph(pDocShell
->GetBaseModel(), MetaNS
))
2167 uno::Reference
<document::XDocumentProperties
> xDocumentProperties
= pDocShell
->getDocProperties();
2168 uno::Reference
<beans::XPropertyContainer
> xPropertyContainer
= xDocumentProperties
->getUserDefinedProperties();
2170 sfx::ClassificationKeyCreator
aKeyCreator(SfxClassificationHelper::getPolicyType());
2171 SfxClassificationHelper
aHelper(xDocumentProperties
);
2173 OUString sHighestClass
= lcl_GetHighestClassificationParagraphClass(GetCursor());
2175 const OUString aClassificationCategory
= svx::classification::getProperty(xPropertyContainer
, aKeyCreator
.makeCategoryNameKey());
2177 if (!aClassificationCategory
.isEmpty())
2179 sHighestClass
= aHelper
.GetHigherClass(sHighestClass
, aClassificationCategory
);
2182 if (aClassificationCategory
!= sHighestClass
)
2184 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(nullptr,
2185 VclMessageType::Question
, VclButtonsType::Ok
,
2186 SwResId(STR_CLASSIFICATION_LEVEL_CHANGED
)));
2190 const SfxClassificationPolicyType eHighestClassType
= SfxClassificationHelper::stringToPolicyType(sHighestClass
);
2192 // Prevent paragraph signature validation since the below changes (f.e. watermarking) are benign.
2193 const bool bOldValidationFlag
= SetParagraphSignatureValidation(false);
2194 comphelper::ScopeGuard
const g([this, bOldValidationFlag
]() {
2195 SetParagraphSignatureValidation(bOldValidationFlag
);
2198 // Check the origin, if "manual" (created via advanced classification dialog),
2199 // then we just need to set the category name.
2200 if (sfx::getCreationOriginProperty(xPropertyContainer
, aKeyCreator
) == sfx::ClassificationCreationOrigin::MANUAL
)
2202 aHelper
.SetBACName(sHighestClass
, eHighestClassType
);
2203 ApplyAdvancedClassification(CollectAdvancedClassification());
2207 SetClassification(sHighestClass
, eHighestClassType
);
2212 void SwEditShell::SetTextFormatColl(SwTextFormatColl
*pFormat
,
2213 const bool bResetListAttrs
,
2216 SwTextFormatColl
*pLocal
= pFormat
? pFormat
: (*GetDoc()->GetTextFormatColls())[0];
2219 SwRewriter aRewriter
;
2221 aRewriter
.AddRule(UndoArg1
, pLocal
->GetName());
2223 GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL
, &aRewriter
);
2224 for(SwPaM
& rPaM
: GetCursor()->GetRingContainer())
2226 if (!rPaM
.HasReadonlySel( GetViewOptions()->IsFormView(), true))
2228 // store previous paragraph style for track changes
2229 OUString sParaStyleName
;
2230 sal_uInt16 nPoolId
= USHRT_MAX
;
2231 SwContentNode
* pCnt
= rPaM
.Start()->GetNode().GetContentNode();
2232 if ( pCnt
&& pCnt
->GetTextNode() && GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() )
2234 const SwTextFormatColl
* pTextFormatColl
= pCnt
->GetTextNode()->GetTextColl();
2235 sal_uInt16 nStylePoolId
= pTextFormatColl
->GetPoolFormatId();
2236 // default paragraph style
2237 if ( nStylePoolId
== RES_POOLCOLL_STANDARD
)
2238 nPoolId
= nStylePoolId
;
2240 sParaStyleName
= pTextFormatColl
->GetName();
2243 // Change the paragraph style to pLocal and remove all direct paragraph formatting.
2244 GetDoc()->SetTextFormatColl(rPaM
, pLocal
, true, bResetListAttrs
, !!(nMode
& SetAttrMode::REMOVE_ALL_ATTR
), GetLayout());
2246 // If there are hints on the nodes which cover the whole node, then remove those, too.
2247 SwPaM
aPaM(*rPaM
.Start(), *rPaM
.End());
2248 if (SwTextNode
* pEndTextNode
= aPaM
.End()->GetNode().GetTextNode())
2250 aPaM
.Start()->SetContent(0);
2251 aPaM
.End()->SetContent(pEndTextNode
->GetText().getLength());
2253 GetDoc()->RstTextAttrs(aPaM
, /*bInclRefToxMark=*/false, /*bExactRange=*/true, GetLayout());
2255 // add redline tracking the previous paragraph style
2256 if ( GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() &&
2257 // multi-paragraph ParagraphFormat redline ranges
2258 // haven't supported by AppendRedline(), yet
2259 // TODO handle multi-paragraph selections, too,
2260 // e.g. by breaking them to single paragraphs
2261 aPaM
.Start()->GetNode() == aPaM
.End()->GetNode() )
2263 SwRangeRedline
* pRedline
= new SwRangeRedline( RedlineType::ParagraphFormat
, aPaM
);
2264 auto const result(GetDoc()->getIDocumentRedlineAccess().AppendRedline( pRedline
, true));
2265 // store original paragraph style to reject formatting change
2266 if ( IDocumentRedlineAccess::AppendResult::IGNORED
!= result
&&
2267 ( nPoolId
== RES_POOLCOLL_STANDARD
|| !sParaStyleName
.isEmpty() ) )
2269 std::unique_ptr
<SwRedlineExtraData_FormatColl
> xExtra
;
2270 xExtra
.reset(new SwRedlineExtraData_FormatColl(sParaStyleName
, nPoolId
, nullptr));
2271 pRedline
->SetExtraData( xExtra
.get() );
2277 GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL
, &aRewriter
);
2281 SwTextFormatColl
* SwEditShell::MakeTextFormatColl(const OUString
& rFormatCollName
,
2282 SwTextFormatColl
* pParent
)
2284 SwTextFormatColl
*pColl
;
2285 if ( pParent
== nullptr )
2286 pParent
= &GetTextFormatColl(0);
2287 pColl
= GetDoc()->MakeTextFormatColl(rFormatCollName
, pParent
);
2288 if ( pColl
== nullptr )
2290 OSL_FAIL( "MakeTextFormatColl failed" );
2296 void SwEditShell::FillByEx(SwTextFormatColl
* pColl
)
2298 SwPaM
* pCursor
= GetCursor();
2299 SwContentNode
* pCnt
= pCursor
->GetPointContentNode();
2300 if (pCnt
->IsTextNode()) // uhm... what nonsense would happen if not?
2301 { // only need properties-node because BREAK/PAGEDESC filtered anyway!
2302 pCnt
= sw::GetParaPropsNode(*GetLayout(), pCursor
->GetPoint()->GetNode());
2304 const SfxItemSet
* pSet
= pCnt
->GetpSwAttrSet();
2308 // JP 05.10.98: Special treatment if one of the attributes Break/PageDesc/NumRule(auto) is
2309 // in the ItemSet. Otherwise there will be too much or wrong processing (NumRules!)
2312 // Do NOT copy AutoNumRules into the template
2313 const SwNumRuleItem
* pItem
;
2314 const SwNumRule
* pRule
= nullptr;
2315 if (SfxItemState::SET
== pSet
->GetItemState(RES_BREAK
, false)
2316 || SfxItemState::SET
== pSet
->GetItemState(RES_PAGEDESC
, false)
2317 || ((pItem
= pSet
->GetItemIfSet(RES_PARATR_NUMRULE
, false))
2318 && nullptr != (pRule
= GetDoc()->FindNumRulePtr(pItem
->GetValue()))
2319 && pRule
->IsAutoRule()))
2321 SfxItemSet
aSet( *pSet
);
2322 aSet
.ClearItem( RES_BREAK
);
2323 aSet
.ClearItem( RES_PAGEDESC
);
2326 || ((pItem
= pSet
->GetItemIfSet(RES_PARATR_NUMRULE
, false))
2327 && nullptr != (pRule
= GetDoc()->FindNumRulePtr(pItem
->GetValue()))
2328 && pRule
->IsAutoRule()))
2329 aSet
.ClearItem( RES_PARATR_NUMRULE
);
2332 GetDoc()->ChgFormat(*pColl
, aSet
);
2335 GetDoc()->ChgFormat(*pColl
, *pSet
);
2338 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */