1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
10 #include <SfxRedactionHelper.hxx>
11 #include <autoredactdialog.hxx>
13 #include <com/sun/star/beans/XPropertySet.hpp>
14 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
15 #include <com/sun/star/drawing/LineStyle.hpp>
16 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
17 #include <com/sun/star/graphic/XGraphic.hpp>
18 #include <com/sun/star/frame/XLayoutManager.hpp>
20 // For page margin related methods
21 #include <com/sun/star/style/XStyle.hpp>
22 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
23 #include <com/sun/star/text/XPageCursor.hpp>
24 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
25 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
28 #include <i18nutil/searchopt.hxx>
29 #include <com/sun/star/util/SearchAlgorithms.hpp>
30 #include <com/sun/star/util/SearchAlgorithms2.hpp>
31 #include <com/sun/star/util/SearchFlags.hpp>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
34 #include <i18nlangtag/languagetag.hxx>
35 #include <unotools/textsearch.hxx>
37 #include <sfx2/request.hxx>
38 #include <sfx2/sfxsids.hrc>
39 #include <sfx2/viewfrm.hxx>
41 #include <svl/eitem.hxx>
42 #include <svl/stritem.hxx>
44 #include <svtools/DocumentToGraphicRenderer.hxx>
46 #include <tools/gen.hxx>
47 #include <tools/stream.hxx>
48 #include <comphelper/diagnose_ex.hxx>
50 #include <vcl/gdimtf.hxx>
51 #include <vcl/graph.hxx>
52 #include <sal/log.hxx>
54 #include <vcl/wmf.hxx>
55 #include <vcl/metaact.hxx>
56 #include <vcl/outdev.hxx>
57 #include <vcl/vcllayout.hxx>
58 #include <o3tl/string_view.hxx>
60 using namespace ::com::sun::star
;
61 using namespace ::com::sun::star::lang
;
62 using namespace ::com::sun::star::uno
;
64 bool SfxRedactionHelper::isRedactMode(const SfxRequest
& rReq
)
66 const SfxItemSet
* pArgs
= rReq
.GetArgs();
69 const SfxBoolItem
* pIsRedactMode
= rReq
.GetArg
<SfxBoolItem
>(SID_IS_REDACT_MODE
);
70 if (pIsRedactMode
&& pIsRedactMode
->GetValue())
77 OUString
SfxRedactionHelper::getStringParam(const SfxRequest
& rReq
, sal_uInt16 nParamId
)
79 OUString sStringParam
;
81 const SfxItemSet
* pArgs
= rReq
.GetArgs();
85 const SfxStringItem
* pStringArg
= rReq
.GetArg
<SfxStringItem
>(nParamId
);
89 sStringParam
= pStringArg
->GetValue();
96 * Roundtrip the gdimetafile to and from WMF
97 * to get rid of the position and size irregularities
98 * We better check the conversion method to see what it
99 * actually does to correct these issues, and do it ourselves.
101 void fixMetaFile(GDIMetaFile
& tmpMtf
)
103 SvMemoryStream
aDestStrm(65535, 65535);
104 ConvertGDIMetaFileToWMF(tmpMtf
, aDestStrm
, nullptr, false);
109 ReadWindowMetafile(aDestStrm
, tmpMtf
);
113 * Sets page margins for a Draw page. Negative values are considered erroneous
115 void setPageMargins(const uno::Reference
<beans::XPropertySet
>& xPageProperySet
,
116 const PageMargins
& aPageMargins
)
118 if (aPageMargins
.nTop
< 0 || aPageMargins
.nBottom
< 0 || aPageMargins
.nLeft
< 0
119 || aPageMargins
.nRight
< 0)
122 xPageProperySet
->setPropertyValue(u
"BorderTop"_ustr
, css::uno::Any(aPageMargins
.nTop
));
123 xPageProperySet
->setPropertyValue(u
"BorderBottom"_ustr
, css::uno::Any(aPageMargins
.nBottom
));
124 xPageProperySet
->setPropertyValue(u
"BorderLeft"_ustr
, css::uno::Any(aPageMargins
.nLeft
));
125 xPageProperySet
->setPropertyValue(u
"BorderRight"_ustr
, css::uno::Any(aPageMargins
.nRight
));
128 // #i10613# Extracted from ImplCheckRect::ImplCreate
129 tools::Rectangle
ImplCalcActionBounds(const MetaAction
& rAct
, const OutputDevice
& rOut
,
130 sal_Int32 nStrStartPos
, sal_Int32 nStrEndPos
)
132 tools::Rectangle aActionBounds
;
134 switch (rAct
.GetType())
136 case MetaActionType::TEXTARRAY
:
138 const MetaTextArrayAction
& rTextAct
= static_cast<const MetaTextArrayAction
&>(rAct
);
139 const OUString
aString(rTextAct
.GetText().copy(rTextAct
.GetIndex(), rTextAct
.GetLen()));
141 if (!aString
.isEmpty())
143 // #105987# ImplLayout takes everything in logical coordinates
144 std::unique_ptr
<SalLayout
> pSalLayout1
= rOut
.ImplLayout(
145 aString
, 0, nStrStartPos
, rTextAct
.GetPoint(), 0, rTextAct
.GetDXArray());
146 std::unique_ptr
<SalLayout
> pSalLayout2
= rOut
.ImplLayout(
147 aString
, 0, nStrEndPos
, rTextAct
.GetPoint(), 0, rTextAct
.GetDXArray());
150 tools::Rectangle
aBoundRect2(rOut
.ImplGetTextBoundRect(*pSalLayout2
));
151 aActionBounds
= rOut
.PixelToLogic(aBoundRect2
);
153 if (pSalLayout1
&& nStrStartPos
> 0)
155 tools::Rectangle
aBoundRect1(rOut
.ImplGetTextBoundRect(*pSalLayout1
));
156 aActionBounds
.SetLeft(rOut
.PixelToLogic(aBoundRect1
).Right());
166 if (!aActionBounds
.IsEmpty())
168 // fdo#40421 limit current action's output to clipped area
169 if (rOut
.IsClipRegion())
170 return rOut
.GetClipRegion().GetBoundRect().Intersection(aActionBounds
);
172 return aActionBounds
;
175 return aActionBounds
;
178 } // End of anon namespace
180 void SfxRedactionHelper::getPageMetaFilesFromDoc(std::vector
<GDIMetaFile
>& aMetaFiles
,
181 std::vector
<::Size
>& aPageSizes
, sal_Int32 nPages
,
182 DocumentToGraphicRenderer
& aRenderer
)
184 for (sal_Int32 nPage
= 1; nPage
<= nPages
; ++nPage
)
186 ::Size aDocumentSizePixel
= aRenderer
.getDocumentSizeInPixels(nPage
);
188 ::Point aCalcPageLogicPos
;
189 ::Size aCalcPageContentSize
;
190 ::Size aLogic
= aRenderer
.getDocumentSizeIn100mm(nPage
, &aLogicPos
, &aCalcPageLogicPos
,
191 &aCalcPageContentSize
);
193 aPageSizes
.push_back(aLogic
);
195 Graphic aGraphic
= aRenderer
.renderToGraphic(nPage
, aDocumentSizePixel
, aDocumentSizePixel
,
196 COL_TRANSPARENT
, true);
197 auto& rGDIMetaFile
= const_cast<GDIMetaFile
&>(aGraphic
.GetGDIMetaFile());
199 // Set preferred map unit and size on the metafile, so the Shape size
200 // will be correct in MM.
202 aMapMode
.SetMapUnit(MapUnit::Map100thMM
);
204 rGDIMetaFile
.SetPrefMapMode(aMapMode
);
205 rGDIMetaFile
.SetPrefSize(aLogic
);
207 fixMetaFile(rGDIMetaFile
);
209 aMetaFiles
.push_back(rGDIMetaFile
);
213 void SfxRedactionHelper::addPagesToDraw(
214 const uno::Reference
<XComponent
>& xComponent
, sal_Int32 nPages
,
215 const std::vector
<GDIMetaFile
>& aMetaFiles
, const std::vector
<::Size
>& aPageSizes
,
216 const PageMargins
& aPageMargins
,
217 const std::vector
<std::pair
<RedactionTarget
, OUString
>>& r_aTableTargets
, bool bIsAutoRedact
)
219 // Access the draw pages
220 uno::Reference
<drawing::XDrawPagesSupplier
> xDrawPagesSupplier(xComponent
, uno::UNO_QUERY
);
221 uno::Reference
<drawing::XDrawPages
> xDrawPages
= xDrawPagesSupplier
->getDrawPages();
223 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(xComponent
, uno::UNO_QUERY
);
225 for (sal_Int32 nPage
= 0; nPage
< nPages
; ++nPage
)
227 const GDIMetaFile
& rGDIMetaFile
= aMetaFiles
[nPage
];
228 Graphic
aGraphic(rGDIMetaFile
);
230 sal_Int32
nPageHeight(aPageSizes
[nPage
].Height());
231 sal_Int32
nPageWidth(aPageSizes
[nPage
].Width());
233 uno::Reference
<graphic::XGraphic
> xGraph
= aGraphic
.GetXGraphic();
234 uno::Reference
<drawing::XDrawPage
> xPage
= xDrawPages
->insertNewByIndex(nPage
);
236 // Set page size & margins
237 uno::Reference
<beans::XPropertySet
> xPageProperySet(xPage
, uno::UNO_QUERY
);
238 xPageProperySet
->setPropertyValue(u
"Height"_ustr
, css::uno::Any(nPageHeight
));
239 xPageProperySet
->setPropertyValue(u
"Width"_ustr
, css::uno::Any(nPageWidth
));
241 setPageMargins(xPageProperySet
, aPageMargins
);
243 // Create and insert the shape
244 uno::Reference
<drawing::XShape
> xShape(
245 xFactory
->createInstance(u
"com.sun.star.drawing.GraphicObjectShape"_ustr
),
247 uno::Reference
<beans::XPropertySet
> xShapeProperySet(xShape
, uno::UNO_QUERY
);
248 xShapeProperySet
->setPropertyValue(u
"Graphic"_ustr
, uno::Any(xGraph
));
249 xShapeProperySet
->setPropertyValue(u
"MoveProtect"_ustr
, uno::Any(true));
250 xShapeProperySet
->setPropertyValue(u
"SizeProtect"_ustr
, uno::Any(true));
254 awt::Size(rGDIMetaFile
.GetPrefSize().Width(), rGDIMetaFile
.GetPrefSize().Height()));
258 if (bIsAutoRedact
&& !r_aTableTargets
.empty())
260 for (const auto& targetPair
: r_aTableTargets
)
262 autoRedactPage(targetPair
.first
, rGDIMetaFile
, xPage
, xComponent
);
267 // Remove the extra page at the beginning
268 uno::Reference
<drawing::XDrawPage
> xPage(xDrawPages
->getByIndex(0), uno::UNO_QUERY_THROW
);
269 xDrawPages
->remove(xPage
);
272 void SfxRedactionHelper::showRedactionToolbar(const SfxViewFrame
* pViewFrame
)
277 Reference
<frame::XFrame
> xFrame
= pViewFrame
->GetFrame().GetFrameInterface();
278 Reference
<css::beans::XPropertySet
> xPropSet(xFrame
, UNO_QUERY
);
279 Reference
<css::frame::XLayoutManager
> xLayoutManager
;
286 Any aValue
= xPropSet
->getPropertyValue(u
"LayoutManager"_ustr
);
287 aValue
>>= xLayoutManager
;
288 xLayoutManager
->createElement(u
"private:resource/toolbar/redactionbar"_ustr
);
289 xLayoutManager
->showElement(u
"private:resource/toolbar/redactionbar"_ustr
);
291 catch (const css::uno::RuntimeException
&)
295 catch (css::uno::Exception
&)
297 TOOLS_WARN_EXCEPTION("sfx.doc", "Exception while trying to show the Redaction Toolbar!");
302 SfxRedactionHelper::getPageMarginsForWriter(const css::uno::Reference
<css::frame::XModel
>& xModel
)
304 PageMargins aPageMargins
= { -1, -1, -1, -1 };
306 Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(xModel
->getCurrentController(),
308 if (!xTextViewCursorSupplier
.is())
310 SAL_WARN("sfx.doc", "Ref to xTextViewCursorSupplier is null in setPageMargins().");
314 Reference
<text::XPageCursor
> xCursor(xTextViewCursorSupplier
->getViewCursor(), UNO_QUERY
);
316 uno::Reference
<beans::XPropertySet
> xPageProperySet(xCursor
, UNO_QUERY
);
317 OUString sPageStyleName
;
318 Any aValue
= xPageProperySet
->getPropertyValue(u
"PageStyleName"_ustr
);
319 aValue
>>= sPageStyleName
;
321 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
322 if (!xStyleFamiliesSupplier
.is())
324 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in setPageMargins().");
327 uno::Reference
<container::XNameAccess
> xStyleFamilies
328 = xStyleFamiliesSupplier
->getStyleFamilies();
330 if (!xStyleFamilies
.is())
333 uno::Reference
<container::XNameAccess
> xPageStyles(
334 xStyleFamilies
->getByName(u
"PageStyles"_ustr
), UNO_QUERY
);
336 if (!xPageStyles
.is())
339 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
342 if (!xPageStyle
.is())
345 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
347 if (!xPageProperties
.is())
350 xPageProperties
->getPropertyValue(u
"LeftMargin"_ustr
) >>= aPageMargins
.nLeft
;
351 xPageProperties
->getPropertyValue(u
"RightMargin"_ustr
) >>= aPageMargins
.nRight
;
352 xPageProperties
->getPropertyValue(u
"TopMargin"_ustr
) >>= aPageMargins
.nTop
;
353 xPageProperties
->getPropertyValue(u
"BottomMargin"_ustr
) >>= aPageMargins
.nBottom
;
359 SfxRedactionHelper::getPageMarginsForCalc(const css::uno::Reference
<css::frame::XModel
>& xModel
)
361 PageMargins aPageMargins
= { -1, -1, -1, -1 };
362 OUString
sPageStyleName(u
"Default"_ustr
);
364 css::uno::Reference
<css::sheet::XSpreadsheetView
> xSpreadsheetView(
365 xModel
->getCurrentController(), UNO_QUERY
);
367 if (!xSpreadsheetView
.is())
369 SAL_WARN("sfx.doc", "Ref to xSpreadsheetView is null in getPageMarginsForCalc().");
373 uno::Reference
<beans::XPropertySet
> xSheetProperties(xSpreadsheetView
->getActiveSheet(),
376 xSheetProperties
->getPropertyValue(u
"PageStyle"_ustr
) >>= sPageStyleName
;
378 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
379 if (!xStyleFamiliesSupplier
.is())
381 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in getPageMarginsForCalc().");
384 uno::Reference
<container::XNameAccess
> xStyleFamilies
385 = xStyleFamiliesSupplier
->getStyleFamilies();
387 if (!xStyleFamilies
.is())
390 uno::Reference
<container::XNameAccess
> xPageStyles(
391 xStyleFamilies
->getByName(u
"PageStyles"_ustr
), UNO_QUERY
);
393 if (!xPageStyles
.is())
396 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
399 if (!xPageStyle
.is())
402 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
404 if (!xPageProperties
.is())
407 xPageProperties
->getPropertyValue(u
"LeftMargin"_ustr
) >>= aPageMargins
.nLeft
;
408 xPageProperties
->getPropertyValue(u
"RightMargin"_ustr
) >>= aPageMargins
.nRight
;
409 xPageProperties
->getPropertyValue(u
"TopMargin"_ustr
) >>= aPageMargins
.nTop
;
410 xPageProperties
->getPropertyValue(u
"BottomMargin"_ustr
) >>= aPageMargins
.nBottom
;
415 void SfxRedactionHelper::searchInMetaFile(const RedactionTarget
& rRedactionTarget
,
416 const GDIMetaFile
& rMtf
,
417 std::vector
<::tools::Rectangle
>& aRedactionRectangles
,
418 const uno::Reference
<XComponent
>& xComponent
)
421 i18nutil::SearchOptions2 aSearchOptions
;
422 fillSearchOptions(aSearchOptions
, rRedactionTarget
);
424 utl::TextSearch
textSearch(aSearchOptions
);
426 OutputDevice
* pOutputDevice
427 = SfxObjectShell::GetShellFromComponent(xComponent
)->GetDocumentRefDev();
428 pOutputDevice
->Push(::vcl::PushFlags::FONT
);
430 MetaAction
* pCurrAct
;
432 for (pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction(); pCurrAct
;
433 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction())
435 // Watch for TEXTARRAY actions.
436 // They contain the text of paragraphs.
437 if (pCurrAct
->GetType() == MetaActionType::TEXTARRAY
)
439 MetaTextArrayAction
* pMetaTextArrayAction
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
441 // Search operation takes place here
442 OUString sText
= pMetaTextArrayAction
->GetText();
443 sal_Int32 nStart
= 0;
444 sal_Int32 nEnd
= sText
.getLength();
446 bool bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
448 // If found the string, add the corresponding rectangle to the collection
451 tools::Rectangle
aNewRect(
452 ImplCalcActionBounds(*pMetaTextArrayAction
, *pOutputDevice
, nStart
, nEnd
));
454 if (!aNewRect
.IsEmpty())
456 // Then increase 10% of the new value to make it look better.
457 auto const adj(aNewRect
.GetHeight() / 20);
458 aNewRect
.AdjustTop(-adj
);
459 aNewRect
.AdjustBottom(adj
);
460 aNewRect
.AdjustRight(adj
); // also add a bit on the right
461 aRedactionRectangles
.push_back(aNewRect
);
464 // Search for the next occurrence
466 nEnd
= sText
.getLength();
467 bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
470 else if (pCurrAct
->GetType() == MetaActionType::FONT
)
472 const MetaFontAction
* pFontAct
= static_cast<const MetaFontAction
*>(pCurrAct
);
473 vcl::Font
const font
{ pFontAct
->GetFont() };
474 pOutputDevice
->SetFont(font
);
478 pOutputDevice
->Pop();
481 void SfxRedactionHelper::addRedactionRectToPage(
482 const uno::Reference
<XComponent
>& xComponent
, const uno::Reference
<drawing::XDrawPage
>& xPage
,
483 const std::vector
<::tools::Rectangle
>& aNewRectangles
)
485 if (!xComponent
.is() || !xPage
.is())
488 if (aNewRectangles
.empty())
491 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(xComponent
, uno::UNO_QUERY
);
493 for (auto const& aNewRectangle
: aNewRectangles
)
495 uno::Reference
<drawing::XShape
> xRectShape(
496 xFactory
->createInstance(u
"com.sun.star.drawing.RectangleShape"_ustr
), uno::UNO_QUERY
);
497 uno::Reference
<beans::XPropertySet
> xRectShapeProperySet(xRectShape
, uno::UNO_QUERY
);
499 xRectShapeProperySet
->setPropertyValue(u
"Name"_ustr
,
500 uno::Any(u
"RectangleRedactionShape"_ustr
));
501 xRectShapeProperySet
->setPropertyValue(u
"FillTransparence"_ustr
,
502 css::uno::Any(static_cast<sal_Int16
>(50)));
503 xRectShapeProperySet
->setPropertyValue(u
"FillColor"_ustr
, css::uno::Any(COL_GRAY7
));
504 xRectShapeProperySet
->setPropertyValue(
505 u
"LineStyle"_ustr
, css::uno::Any(css::drawing::LineStyle::LineStyle_NONE
));
507 xRectShape
->setSize(awt::Size(aNewRectangle
.GetWidth(), aNewRectangle
.GetHeight()));
508 xRectShape
->setPosition(awt::Point(aNewRectangle
.Left(), aNewRectangle
.Top()));
510 xPage
->add(xRectShape
);
514 void SfxRedactionHelper::autoRedactPage(const RedactionTarget
& rRedactionTarget
,
515 const GDIMetaFile
& rGDIMetaFile
,
516 const uno::Reference
<drawing::XDrawPage
>& xPage
,
517 const uno::Reference
<XComponent
>& xComponent
)
519 if (rRedactionTarget
.sContent
.isEmpty())
522 // Search for the redaction strings, and get the rectangle coordinates
523 std::vector
<::tools::Rectangle
> aRedactionRectangles
;
524 searchInMetaFile(rRedactionTarget
, rGDIMetaFile
, aRedactionRectangles
, xComponent
);
526 // Add the redaction rectangles to the page
527 addRedactionRectToPage(xComponent
, xPage
, aRedactionRectangles
);
532 const LanguageTag
& GetAppLanguageTag() { return Application::GetSettings().GetLanguageTag(); }
535 void SfxRedactionHelper::fillSearchOptions(i18nutil::SearchOptions2
& rSearchOpt
,
536 const RedactionTarget
& rTarget
)
538 if (rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_REGEX
539 || rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
541 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::REGEXP
;
545 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
548 rSearchOpt
.Locale
= GetAppLanguageTag().getLocale();
549 if (rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
551 auto nPredefIndex
= o3tl::toUInt32(o3tl::getToken(rTarget
.sContent
, 0, ';'));
552 rSearchOpt
.searchString
= m_aPredefinedTargets
[nPredefIndex
];
555 rSearchOpt
.searchString
= rTarget
.sContent
;
557 rSearchOpt
.replaceString
.clear();
559 if (!rTarget
.bCaseSensitive
&& rTarget
.sType
!= RedactionTargetType::REDACTION_TARGET_REGEX
560 && rTarget
.sType
!= RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
561 rSearchOpt
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
562 if (rTarget
.bWholeWords
)
563 rSearchOpt
.searchFlag
|= util::SearchFlags::NORM_WORD_ONLY
;
566 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */