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("BorderTop", css::uno::Any(aPageMargins
.nTop
));
123 xPageProperySet
->setPropertyValue("BorderBottom", css::uno::Any(aPageMargins
.nBottom
));
124 xPageProperySet
->setPropertyValue("BorderLeft", css::uno::Any(aPageMargins
.nLeft
));
125 xPageProperySet
->setPropertyValue("BorderRight", 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 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("Height", css::uno::Any(nPageHeight
));
239 xPageProperySet
->setPropertyValue("Width", css::uno::Any(nPageWidth
));
241 setPageMargins(xPageProperySet
, aPageMargins
);
243 // Create and insert the shape
244 uno::Reference
<drawing::XShape
> xShape(
245 xFactory
->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY
);
246 uno::Reference
<beans::XPropertySet
> xShapeProperySet(xShape
, uno::UNO_QUERY
);
247 xShapeProperySet
->setPropertyValue("Graphic", uno::Any(xGraph
));
248 xShapeProperySet
->setPropertyValue("MoveProtect", uno::Any(true));
249 xShapeProperySet
->setPropertyValue("SizeProtect", uno::Any(true));
253 awt::Size(rGDIMetaFile
.GetPrefSize().Width(), rGDIMetaFile
.GetPrefSize().Height()));
257 if (bIsAutoRedact
&& !r_aTableTargets
.empty())
259 for (const auto& targetPair
: r_aTableTargets
)
261 autoRedactPage(targetPair
.first
, rGDIMetaFile
, xPage
, xComponent
);
266 // Remove the extra page at the beginning
267 uno::Reference
<drawing::XDrawPage
> xPage(xDrawPages
->getByIndex(0), uno::UNO_QUERY_THROW
);
268 xDrawPages
->remove(xPage
);
271 void SfxRedactionHelper::showRedactionToolbar(const SfxViewFrame
* pViewFrame
)
276 Reference
<frame::XFrame
> xFrame
= pViewFrame
->GetFrame().GetFrameInterface();
277 Reference
<css::beans::XPropertySet
> xPropSet(xFrame
, UNO_QUERY
);
278 Reference
<css::frame::XLayoutManager
> xLayoutManager
;
285 Any aValue
= xPropSet
->getPropertyValue("LayoutManager");
286 aValue
>>= xLayoutManager
;
287 xLayoutManager
->createElement("private:resource/toolbar/redactionbar");
288 xLayoutManager
->showElement("private:resource/toolbar/redactionbar");
290 catch (const css::uno::RuntimeException
&)
294 catch (css::uno::Exception
&)
296 TOOLS_WARN_EXCEPTION("sfx.doc", "Exception while trying to show the Redaction Toolbar!");
301 SfxRedactionHelper::getPageMarginsForWriter(const css::uno::Reference
<css::frame::XModel
>& xModel
)
303 PageMargins aPageMargins
= { -1, -1, -1, -1 };
305 Reference
<text::XTextViewCursorSupplier
> xTextViewCursorSupplier(xModel
->getCurrentController(),
307 if (!xTextViewCursorSupplier
.is())
309 SAL_WARN("sfx.doc", "Ref to xTextViewCursorSupplier is null in setPageMargins().");
313 Reference
<text::XPageCursor
> xCursor(xTextViewCursorSupplier
->getViewCursor(), UNO_QUERY
);
315 uno::Reference
<beans::XPropertySet
> xPageProperySet(xCursor
, UNO_QUERY
);
316 OUString sPageStyleName
;
317 Any aValue
= xPageProperySet
->getPropertyValue("PageStyleName");
318 aValue
>>= sPageStyleName
;
320 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
321 if (!xStyleFamiliesSupplier
.is())
323 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in setPageMargins().");
326 uno::Reference
<container::XNameAccess
> xStyleFamilies
327 = xStyleFamiliesSupplier
->getStyleFamilies();
329 if (!xStyleFamilies
.is())
332 uno::Reference
<container::XNameAccess
> xPageStyles(xStyleFamilies
->getByName("PageStyles"),
335 if (!xPageStyles
.is())
338 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
341 if (!xPageStyle
.is())
344 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
346 if (!xPageProperties
.is())
349 xPageProperties
->getPropertyValue("LeftMargin") >>= aPageMargins
.nLeft
;
350 xPageProperties
->getPropertyValue("RightMargin") >>= aPageMargins
.nRight
;
351 xPageProperties
->getPropertyValue("TopMargin") >>= aPageMargins
.nTop
;
352 xPageProperties
->getPropertyValue("BottomMargin") >>= aPageMargins
.nBottom
;
358 SfxRedactionHelper::getPageMarginsForCalc(const css::uno::Reference
<css::frame::XModel
>& xModel
)
360 PageMargins aPageMargins
= { -1, -1, -1, -1 };
361 OUString
sPageStyleName("Default");
363 css::uno::Reference
<css::sheet::XSpreadsheetView
> xSpreadsheetView(
364 xModel
->getCurrentController(), UNO_QUERY
);
366 if (!xSpreadsheetView
.is())
368 SAL_WARN("sfx.doc", "Ref to xSpreadsheetView is null in getPageMarginsForCalc().");
372 uno::Reference
<beans::XPropertySet
> xSheetProperties(xSpreadsheetView
->getActiveSheet(),
375 xSheetProperties
->getPropertyValue("PageStyle") >>= sPageStyleName
;
377 Reference
<css::style::XStyleFamiliesSupplier
> xStyleFamiliesSupplier(xModel
, UNO_QUERY
);
378 if (!xStyleFamiliesSupplier
.is())
380 SAL_WARN("sfx.doc", "Ref to xStyleFamiliesSupplier is null in getPageMarginsForCalc().");
383 uno::Reference
<container::XNameAccess
> xStyleFamilies
384 = xStyleFamiliesSupplier
->getStyleFamilies();
386 if (!xStyleFamilies
.is())
389 uno::Reference
<container::XNameAccess
> xPageStyles(xStyleFamilies
->getByName("PageStyles"),
392 if (!xPageStyles
.is())
395 uno::Reference
<css::style::XStyle
> xPageStyle(xPageStyles
->getByName(sPageStyleName
),
398 if (!xPageStyle
.is())
401 uno::Reference
<beans::XPropertySet
> xPageProperties(xPageStyle
, uno::UNO_QUERY
);
403 if (!xPageProperties
.is())
406 xPageProperties
->getPropertyValue("LeftMargin") >>= aPageMargins
.nLeft
;
407 xPageProperties
->getPropertyValue("RightMargin") >>= aPageMargins
.nRight
;
408 xPageProperties
->getPropertyValue("TopMargin") >>= aPageMargins
.nTop
;
409 xPageProperties
->getPropertyValue("BottomMargin") >>= aPageMargins
.nBottom
;
414 void SfxRedactionHelper::searchInMetaFile(const RedactionTarget
& rRedactionTarget
,
415 const GDIMetaFile
& rMtf
,
416 std::vector
<::tools::Rectangle
>& aRedactionRectangles
,
417 const uno::Reference
<XComponent
>& xComponent
)
420 i18nutil::SearchOptions2 aSearchOptions
;
421 fillSearchOptions(aSearchOptions
, rRedactionTarget
);
423 utl::TextSearch
textSearch(aSearchOptions
);
424 static tools::Long aLastFontHeight
= 0;
426 MetaAction
* pCurrAct
;
428 for (pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).FirstAction(); pCurrAct
;
429 pCurrAct
= const_cast<GDIMetaFile
&>(rMtf
).NextAction())
431 // Watch for TEXTARRAY actions.
432 // They contain the text of paragraphs.
433 if (pCurrAct
->GetType() == MetaActionType::TEXTARRAY
)
435 MetaTextArrayAction
* pMetaTextArrayAction
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
437 // Search operation takes place here
438 OUString sText
= pMetaTextArrayAction
->GetText();
439 sal_Int32 nStart
= 0;
440 sal_Int32 nEnd
= sText
.getLength();
442 bool bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
444 // If found the string, add the corresponding rectangle to the collection
447 OutputDevice
* pOutputDevice
448 = SfxObjectShell::GetShellFromComponent(xComponent
)->GetDocumentRefDev();
449 tools::Rectangle
aNewRect(
450 ImplCalcActionBounds(*pMetaTextArrayAction
, *pOutputDevice
, nStart
, nEnd
));
452 if (!aNewRect
.IsEmpty())
454 // Calculate the difference between current wrong value and value should it be.
455 // Add the difference to current value.
456 // Then increase 10% of the new value to make it look better.
457 aNewRect
.SetTop(aNewRect
.Bottom() - aLastFontHeight
- aLastFontHeight
/ 10);
458 aRedactionRectangles
.push_back(aNewRect
);
461 // Search for the next occurrence
463 nEnd
= sText
.getLength();
464 bFound
= textSearch
.SearchForward(sText
, &nStart
, &nEnd
);
467 else if (pCurrAct
->GetType() == MetaActionType::FONT
)
469 const MetaFontAction
* pFontAct
= static_cast<const MetaFontAction
*>(pCurrAct
);
470 aLastFontHeight
= pFontAct
->GetFont().GetFontSize().getHeight();
475 void SfxRedactionHelper::addRedactionRectToPage(
476 const uno::Reference
<XComponent
>& xComponent
, const uno::Reference
<drawing::XDrawPage
>& xPage
,
477 const std::vector
<::tools::Rectangle
>& aNewRectangles
)
479 if (!xComponent
.is() || !xPage
.is())
482 if (aNewRectangles
.empty())
485 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory(xComponent
, uno::UNO_QUERY
);
487 for (auto const& aNewRectangle
: aNewRectangles
)
489 uno::Reference
<drawing::XShape
> xRectShape(
490 xFactory
->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY
);
491 uno::Reference
<beans::XPropertySet
> xRectShapeProperySet(xRectShape
, uno::UNO_QUERY
);
493 xRectShapeProperySet
->setPropertyValue("Name",
494 uno::Any(OUString("RectangleRedactionShape")));
495 xRectShapeProperySet
->setPropertyValue("FillTransparence",
496 css::uno::Any(static_cast<sal_Int16
>(50)));
497 xRectShapeProperySet
->setPropertyValue("FillColor", css::uno::Any(COL_GRAY7
));
498 xRectShapeProperySet
->setPropertyValue(
499 "LineStyle", css::uno::Any(css::drawing::LineStyle::LineStyle_NONE
));
501 xRectShape
->setSize(awt::Size(aNewRectangle
.GetWidth(), aNewRectangle
.GetHeight()));
502 xRectShape
->setPosition(awt::Point(aNewRectangle
.Left(), aNewRectangle
.Top()));
504 xPage
->add(xRectShape
);
508 void SfxRedactionHelper::autoRedactPage(const RedactionTarget
& rRedactionTarget
,
509 const GDIMetaFile
& rGDIMetaFile
,
510 const uno::Reference
<drawing::XDrawPage
>& xPage
,
511 const uno::Reference
<XComponent
>& xComponent
)
513 if (rRedactionTarget
.sContent
.isEmpty())
516 // Search for the redaction strings, and get the rectangle coordinates
517 std::vector
<::tools::Rectangle
> aRedactionRectangles
;
518 searchInMetaFile(rRedactionTarget
, rGDIMetaFile
, aRedactionRectangles
, xComponent
);
520 // Add the redaction rectangles to the page
521 addRedactionRectToPage(xComponent
, xPage
, aRedactionRectangles
);
526 const LanguageTag
& GetAppLanguageTag() { return Application::GetSettings().GetLanguageTag(); }
529 void SfxRedactionHelper::fillSearchOptions(i18nutil::SearchOptions2
& rSearchOpt
,
530 const RedactionTarget
& rTarget
)
532 if (rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_REGEX
533 || rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
535 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::REGEXP
;
539 rSearchOpt
.AlgorithmType2
= util::SearchAlgorithms2::ABSOLUTE
;
542 rSearchOpt
.Locale
= GetAppLanguageTag().getLocale();
543 if (rTarget
.sType
== RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
545 auto nPredefIndex
= o3tl::toUInt32(o3tl::getToken(rTarget
.sContent
, 0, ';'));
546 rSearchOpt
.searchString
= m_aPredefinedTargets
[nPredefIndex
];
549 rSearchOpt
.searchString
= rTarget
.sContent
;
551 rSearchOpt
.replaceString
.clear();
553 if (!rTarget
.bCaseSensitive
&& rTarget
.sType
!= RedactionTargetType::REDACTION_TARGET_REGEX
554 && rTarget
.sType
!= RedactionTargetType::REDACTION_TARGET_PREDEFINED
)
555 rSearchOpt
.transliterateFlags
|= TransliterationFlags::IGNORE_CASE
;
556 if (rTarget
.bWholeWords
)
557 rSearchOpt
.searchFlag
|= util::SearchFlags::NORM_WORD_ONLY
;
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */