/src/libreoffice/sc/source/ui/view/output2.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <scitems.hxx> |
21 | | #include <editeng/eeitem.hxx> |
22 | | |
23 | | #include <editeng/adjustitem.hxx> |
24 | | #include <svx/algitem.hxx> |
25 | | #include <editeng/brushitem.hxx> |
26 | | #include <svtools/colorcfg.hxx> |
27 | | #include <editeng/colritem.hxx> |
28 | | #include <editeng/charreliefitem.hxx> |
29 | | #include <editeng/crossedoutitem.hxx> |
30 | | #include <editeng/contouritem.hxx> |
31 | | #include <editeng/editobj.hxx> |
32 | | #include <editeng/editstat.hxx> |
33 | | #include <editeng/emphasismarkitem.hxx> |
34 | | #include <editeng/fhgtitem.hxx> |
35 | | #include <editeng/forbiddenruleitem.hxx> |
36 | | #include <editeng/frmdiritem.hxx> |
37 | | #include <editeng/justifyitem.hxx> |
38 | | #include <svx/rotmodit.hxx> |
39 | | #include <editeng/udlnitem.hxx> |
40 | | #include <editeng/unolingu.hxx> |
41 | | #include <editeng/fontitem.hxx> |
42 | | #include <editeng/postitem.hxx> |
43 | | #include <editeng/shdditem.hxx> |
44 | | #include <editeng/wghtitem.hxx> |
45 | | #include <editeng/wrlmitem.hxx> |
46 | | #include <formula/errorcodes.hxx> |
47 | | #include <svl/numformat.hxx> |
48 | | #include <svl/zforlist.hxx> |
49 | | #include <svl/zformat.hxx> |
50 | | #include <vcl/kernarray.hxx> |
51 | | #include <vcl/svapp.hxx> |
52 | | #include <vcl/metric.hxx> |
53 | | #include <vcl/outdev.hxx> |
54 | | #include <vcl/pdfextoutdevdata.hxx> |
55 | | #include <vcl/settings.hxx> |
56 | | #include <vcl/glyphitem.hxx> |
57 | | #include <vcl/glyphitemcache.hxx> |
58 | | #include <sal/log.hxx> |
59 | | #include <unotools/charclass.hxx> |
60 | | #include <osl/diagnose.h> |
61 | | |
62 | | #include <output.hxx> |
63 | | #include <document.hxx> |
64 | | #include <formulacell.hxx> |
65 | | #include <attrib.hxx> |
66 | | #include <patattr.hxx> |
67 | | #include <cellform.hxx> |
68 | | #include <editutil.hxx> |
69 | | #include <progress.hxx> |
70 | | #include <scmod.hxx> |
71 | | #include <fillinfo.hxx> |
72 | | #include <stlsheet.hxx> |
73 | | #include <spellcheckcontext.hxx> |
74 | | #include <scopetools.hxx> |
75 | | #include <tabvwsh.hxx> |
76 | | |
77 | | #include <com/sun/star/i18n/DirectionProperty.hpp> |
78 | | #include <comphelper/scopeguard.hxx> |
79 | | #include <comphelper/string.hxx> |
80 | | |
81 | | #include <memory> |
82 | | #include <vector> |
83 | | |
84 | | #include <math.h> |
85 | | |
86 | | using namespace com::sun::star; |
87 | | |
88 | | //! Merge Autofilter width with column.cxx |
89 | 0 | #define DROPDOWN_BITMAP_SIZE 18 |
90 | | |
91 | 0 | #define DRAWTEXT_MAX 32767 |
92 | | |
93 | | const sal_uInt16 SC_SHRINKAGAIN_MAX = 7; |
94 | | constexpr auto HMM_PER_TWIPS = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100); |
95 | | |
96 | | class ScDrawStringsVars |
97 | | { |
98 | | ScOutputData* pOutput; // connection |
99 | | |
100 | | const ScPatternAttr* pPattern; // attribute |
101 | | const SfxItemSet* pCondSet; // from conditional formatting |
102 | | const SfxItemSet* pTableSet; // from table formatting |
103 | | |
104 | | vcl::Font aFont; // created from attributes |
105 | | FontMetric aMetric; |
106 | | tools::Long nAscentPixel; // always pixels |
107 | | SvxCellOrientation eAttrOrient; |
108 | | SvxCellHorJustify eAttrHorJust; |
109 | | SvxCellVerJustify eAttrVerJust; |
110 | | SvxCellJustifyMethod eAttrHorJustMethod; |
111 | | const SvxMarginItem* pMargin; |
112 | | sal_uInt16 nIndent; |
113 | | bool bRotated; |
114 | | |
115 | | OUString aString; // contents |
116 | | Size aTextSize; |
117 | | tools::Long nOriginalWidth; |
118 | | tools::Long nMaxDigitWidth; |
119 | | tools::Long nSignWidth; |
120 | | tools::Long nDotWidth; |
121 | | tools::Long nExpWidth; |
122 | | |
123 | | ScRefCellValue maLastCell; |
124 | | sal_uLong nValueFormat; |
125 | | bool bLineBreak; |
126 | | bool bRepeat; |
127 | | bool bShrink; |
128 | | |
129 | | bool bPixelToLogic; |
130 | | bool bCellContrast; |
131 | | |
132 | | Color aBackConfigColor; // used for ScPatternAttr::GetFont calls |
133 | | Color aTextConfigColor; |
134 | | sal_Int32 nRepeatPos; |
135 | | sal_Unicode nRepeatChar; |
136 | | |
137 | | public: |
138 | | ScDrawStringsVars(ScOutputData* pData, bool bPTL); |
139 | | |
140 | | // SetPattern = ex-SetVars |
141 | | // SetPatternSimple: without Font |
142 | | |
143 | | void SetPattern( |
144 | | const ScPatternAttr* pNew, const SfxItemSet* pSet, const SfxItemSet* pTSet, const ScRefCellValue& rCell, |
145 | | SvtScriptType nScript ); |
146 | | |
147 | | void SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet ); |
148 | | |
149 | | bool SetText( const ScRefCellValue& rCell ); // TRUE -> drop pOldPattern |
150 | | void SetHashText(); |
151 | | bool SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth ); |
152 | | void SetAutoText( const OUString& rAutoText ); |
153 | | |
154 | 0 | SvxCellOrientation GetOrient() const { return eAttrOrient; } |
155 | 0 | SvxCellHorJustify GetHorJust() const { return eAttrHorJust; } |
156 | 0 | SvxCellVerJustify GetVerJust() const { return eAttrVerJust; } |
157 | 0 | SvxCellJustifyMethod GetHorJustMethod() const { return eAttrHorJustMethod; } |
158 | 0 | const SvxMarginItem* GetMargin() const { return pMargin; } |
159 | | |
160 | 0 | sal_uInt16 GetLeftTotal() const { return pMargin->GetLeftMargin() + nIndent; } |
161 | 0 | sal_uInt16 GetRightTotal() const { return pMargin->GetRightMargin() + nIndent; } |
162 | | |
163 | 0 | const OUString& GetString() const { return aString; } |
164 | 0 | const Size& GetTextSize() const { return aTextSize; } |
165 | 0 | tools::Long GetOriginalWidth() const { return nOriginalWidth; } |
166 | | tools::Long GetFmtTextWidth(const OUString& rString); |
167 | | |
168 | | // Get the effective number format, including formula result types. |
169 | | // This assumes that a formula cell has already been calculated. |
170 | 0 | sal_uLong GetResultValueFormat() const { return nValueFormat;} |
171 | | |
172 | 0 | bool GetLineBreak() const { return bLineBreak; } |
173 | 0 | bool IsRepeat() const { return bRepeat; } |
174 | 0 | bool IsShrink() const { return bShrink; } |
175 | | void RepeatToFill( tools::Long nColWidth ); |
176 | | |
177 | 0 | tools::Long GetAscent() const { return nAscentPixel; } |
178 | 0 | bool IsRotated() const { return bRotated; } |
179 | | |
180 | | void SetShrinkScale( tools::Long nScale, SvtScriptType nScript ); |
181 | | |
182 | 0 | bool HasCondHeight() const { return pCondSet && SfxItemState::SET == |
183 | 0 | pCondSet->GetItemState( ATTR_FONT_HEIGHT ); } |
184 | | |
185 | | bool HasEditCharacters() const; |
186 | | |
187 | | // ScOutputData::LayoutStrings() usually triggers a number of calls that require |
188 | | // to lay out the text, which is relatively slow, so cache that operation. |
189 | | const SalLayoutGlyphs* GetLayoutGlyphs(const OUString& rString) const |
190 | 0 | { |
191 | 0 | return SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOutput->pFmtDevice, rString); |
192 | 0 | } |
193 | | |
194 | | private: |
195 | | tools::Long GetMaxDigitWidth(); // in logic units |
196 | | tools::Long GetSignWidth(); |
197 | | tools::Long GetDotWidth(); |
198 | | tools::Long GetExpWidth(); |
199 | | void TextChanged(); |
200 | | }; |
201 | | |
202 | | ScDrawStringsVars::ScDrawStringsVars(ScOutputData* pData, bool bPTL) : |
203 | 0 | pOutput ( pData ), |
204 | 0 | pPattern ( nullptr ), |
205 | 0 | pCondSet ( nullptr ), |
206 | 0 | pTableSet ( nullptr ), |
207 | 0 | nAscentPixel(0), |
208 | 0 | eAttrOrient ( SvxCellOrientation::Standard ), |
209 | 0 | eAttrHorJust( SvxCellHorJustify::Standard ), |
210 | 0 | eAttrVerJust( SvxCellVerJustify::Bottom ), |
211 | 0 | eAttrHorJustMethod( SvxCellJustifyMethod::Auto ), |
212 | 0 | pMargin ( nullptr ), |
213 | 0 | nIndent ( 0 ), |
214 | 0 | bRotated ( false ), |
215 | 0 | nOriginalWidth( 0 ), |
216 | 0 | nMaxDigitWidth( 0 ), |
217 | 0 | nSignWidth( 0 ), |
218 | 0 | nDotWidth( 0 ), |
219 | 0 | nExpWidth( 0 ), |
220 | 0 | nValueFormat( 0 ), |
221 | 0 | bLineBreak ( false ), |
222 | 0 | bRepeat ( false ), |
223 | 0 | bShrink ( false ), |
224 | 0 | bPixelToLogic( bPTL ), |
225 | 0 | nRepeatPos( -1 ), |
226 | 0 | nRepeatChar( 0x0 ) |
227 | 0 | { |
228 | 0 | bCellContrast = pOutput->mbUseStyleColor && |
229 | 0 | Application::GetSettings().GetStyleSettings().GetHighContrastMode(); |
230 | |
|
231 | 0 | const svtools::ColorConfig& rColorConfig = ScModule::get()->GetColorConfig(); |
232 | 0 | aBackConfigColor = rColorConfig.GetColorValue(svtools::DOCCOLOR).nColor; |
233 | 0 | aTextConfigColor = rColorConfig.GetColorValue(svtools::FONTCOLOR).nColor; |
234 | 0 | } |
235 | | |
236 | | void ScDrawStringsVars::SetShrinkScale( tools::Long nScale, SvtScriptType nScript ) |
237 | 0 | { |
238 | | // text remains valid, size is updated |
239 | |
|
240 | 0 | OutputDevice* pDev = pOutput->mpDev; |
241 | 0 | OutputDevice* pRefDevice = pOutput->mpRefDevice; |
242 | 0 | OutputDevice* pFmtDevice = pOutput->pFmtDevice; |
243 | | |
244 | | // call GetFont with a modified fraction, use only the height |
245 | |
|
246 | 0 | double fFraction = double(nScale) / 100; |
247 | 0 | if ( !bPixelToLogic ) |
248 | 0 | fFraction *= pOutput->mfZoomY; |
249 | 0 | vcl::Font aTmpFont; |
250 | 0 | pPattern->fillFontOnly(aTmpFont, pFmtDevice, &fFraction, pCondSet, pTableSet, nScript); |
251 | | // only need font height |
252 | 0 | tools::Long nNewHeight = aTmpFont.GetFontHeight(); |
253 | 0 | if ( nNewHeight > 0 ) |
254 | 0 | aFont.SetFontHeight( nNewHeight ); |
255 | | |
256 | | // set font and dependent variables as in SetPattern |
257 | |
|
258 | 0 | pDev->SetFont( aFont ); |
259 | 0 | if ( pFmtDevice != pDev ) |
260 | 0 | pFmtDevice->SetFont( aFont ); |
261 | |
|
262 | 0 | aMetric = pFmtDevice->GetFontMetric(); |
263 | 0 | if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 ) |
264 | 0 | { |
265 | 0 | OutputDevice* pDefaultDev = Application::GetDefaultDevice(); |
266 | 0 | MapMode aOld = pDefaultDev->GetMapMode(); |
267 | 0 | pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() ); |
268 | 0 | aMetric = pDefaultDev->GetFontMetric( aFont ); |
269 | 0 | pDefaultDev->SetMapMode( aOld ); |
270 | 0 | } |
271 | |
|
272 | 0 | nAscentPixel = aMetric.GetAscent(); |
273 | 0 | if ( bPixelToLogic ) |
274 | 0 | nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height(); |
275 | |
|
276 | 0 | SetAutoText( aString ); // same text again, to get text size |
277 | 0 | } |
278 | | |
279 | | namespace { |
280 | | |
281 | | template<typename ItemType, typename EnumType> |
282 | | EnumType lcl_GetValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet) |
283 | 0 | { |
284 | 0 | const ItemType& rItem = static_cast<const ItemType&>(rPattern.GetItem(nWhich, pCondSet)); |
285 | 0 | return static_cast<EnumType>(rItem.GetValue()); |
286 | 0 | } Unexecuted instantiation: output2.cxx:SvxFrameDirection (anonymous namespace)::lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:bool (anonymous namespace)::lcl_GetValue<SfxBoolItem, bool>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:SvxCellJustifyMethod (anonymous namespace)::lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:SvxCellHorJustify (anonymous namespace)::lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:SvxCellVerJustify (anonymous namespace)::lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:unsigned short (anonymous namespace)::lcl_GetValue<ScIndentItem, unsigned short>(ScPatternAttr const&, unsigned short, SfxItemSet const*) Unexecuted instantiation: output2.cxx:o3tl::strong_int<int, FractionTag<100> > (anonymous namespace)::lcl_GetValue<ScRotateValueItem, o3tl::strong_int<int, FractionTag<100> > >(ScPatternAttr const&, unsigned short, SfxItemSet const*) |
287 | | |
288 | | bool lcl_GetBoolValue(const ScPatternAttr& rPattern, sal_uInt16 nWhich, const SfxItemSet* pCondSet) |
289 | 0 | { |
290 | 0 | return lcl_GetValue<SfxBoolItem, bool>(rPattern, nWhich, pCondSet); |
291 | 0 | } |
292 | | |
293 | | } |
294 | | |
295 | | static bool lcl_isNumberFormatText(const ScDocument* pDoc, SCCOL nCellX, SCROW nCellY, SCTAB mnTab ) |
296 | 0 | { |
297 | 0 | sal_uInt32 nCurrentNumberFormat = pDoc->GetNumberFormat( nCellX, nCellY, mnTab ); |
298 | 0 | SvNumberFormatter* pNumberFormatter = pDoc->GetFormatTable(); |
299 | 0 | return pNumberFormatter->GetType( nCurrentNumberFormat ) == SvNumFormatType::TEXT; |
300 | 0 | } |
301 | | |
302 | | void ScDrawStringsVars::SetPattern( const ScPatternAttr* pNew, const SfxItemSet* pSet, const SfxItemSet* pTSet, const ScRefCellValue& rCell, SvtScriptType nScript ) |
303 | 0 | { |
304 | 0 | nMaxDigitWidth = 0; |
305 | 0 | nSignWidth = 0; |
306 | 0 | nDotWidth = 0; |
307 | 0 | nExpWidth = 0; |
308 | |
|
309 | 0 | pPattern = pNew; |
310 | 0 | pCondSet = pSet; |
311 | 0 | pTableSet = pTSet; |
312 | | |
313 | | // evaluate pPattern |
314 | |
|
315 | 0 | OutputDevice* pDev = pOutput->mpDev; |
316 | 0 | OutputDevice* pRefDevice = pOutput->mpRefDevice; |
317 | 0 | OutputDevice* pFmtDevice = pOutput->pFmtDevice; |
318 | | |
319 | | // font |
320 | |
|
321 | 0 | ScAutoFontColorMode eColorMode; |
322 | 0 | if ( pOutput->mbUseStyleColor ) |
323 | 0 | { |
324 | 0 | if ( pOutput->mbForceAutoColor ) |
325 | 0 | eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreAll : ScAutoFontColorMode::IgnoreFont; |
326 | 0 | else |
327 | 0 | eColorMode = bCellContrast ? ScAutoFontColorMode::IgnoreBack : ScAutoFontColorMode::Display; |
328 | 0 | } |
329 | 0 | else |
330 | 0 | eColorMode = ScAutoFontColorMode::Print; |
331 | |
|
332 | 0 | if (bPixelToLogic) |
333 | 0 | pPattern->fillFont(aFont, eColorMode, pFmtDevice, nullptr, pCondSet, pTableSet, nScript, &aBackConfigColor, &aTextConfigColor); |
334 | 0 | else |
335 | 0 | pPattern->fillFont(aFont, eColorMode, pFmtDevice, &pOutput->mfZoomY, pCondSet, pTableSet, nScript, &aBackConfigColor, &aTextConfigColor ); |
336 | |
|
337 | 0 | aFont.SetAlignment(ALIGN_BASELINE); |
338 | | |
339 | | // orientation |
340 | |
|
341 | 0 | eAttrOrient = pPattern->GetCellOrientation( pCondSet ); |
342 | | |
343 | | // alignment |
344 | |
|
345 | 0 | eAttrHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet ).GetValue(); |
346 | |
|
347 | 0 | eAttrVerJust = pPattern->GetItem( ATTR_VER_JUSTIFY, pCondSet ).GetValue(); |
348 | 0 | if ( eAttrVerJust == SvxCellVerJustify::Standard ) |
349 | 0 | eAttrVerJust = SvxCellVerJustify::Bottom; |
350 | | |
351 | | // justification method |
352 | |
|
353 | 0 | eAttrHorJustMethod = lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet); |
354 | | |
355 | | // line break |
356 | |
|
357 | 0 | bLineBreak = pPattern->GetItem( ATTR_LINEBREAK, pCondSet ).GetValue(); |
358 | | |
359 | | // handle "repeat" alignment |
360 | |
|
361 | 0 | bRepeat = ( eAttrHorJust == SvxCellHorJustify::Repeat ); |
362 | 0 | if ( bRepeat ) |
363 | 0 | { |
364 | | // "repeat" disables rotation (before constructing the font) |
365 | 0 | eAttrOrient = SvxCellOrientation::Standard; |
366 | | |
367 | | // #i31843# "repeat" with "line breaks" is treated as default alignment (but rotation is still disabled) |
368 | 0 | if ( bLineBreak ) |
369 | 0 | eAttrHorJust = SvxCellHorJustify::Standard; |
370 | 0 | } |
371 | |
|
372 | 0 | sal_Int16 nRot; |
373 | 0 | switch (eAttrOrient) |
374 | 0 | { |
375 | 0 | case SvxCellOrientation::Standard: |
376 | 0 | nRot = 0; |
377 | 0 | bRotated = pPattern->GetItem( ATTR_ROTATE_VALUE, pCondSet ).GetValue() != 0_deg100 && |
378 | 0 | !bRepeat; |
379 | 0 | break; |
380 | 0 | case SvxCellOrientation::Stacked: |
381 | 0 | nRot = 0; |
382 | 0 | bRotated = false; |
383 | 0 | break; |
384 | 0 | case SvxCellOrientation::TopBottom: |
385 | 0 | nRot = 2700; |
386 | 0 | bRotated = false; |
387 | 0 | break; |
388 | 0 | case SvxCellOrientation::BottomUp: |
389 | 0 | nRot = 900; |
390 | 0 | bRotated = false; |
391 | 0 | break; |
392 | 0 | default: |
393 | 0 | OSL_FAIL("Invalid SvxCellOrientation value"); |
394 | 0 | nRot = 0; |
395 | 0 | bRotated = false; |
396 | 0 | break; |
397 | 0 | } |
398 | 0 | aFont.SetOrientation( Degree10(nRot) ); |
399 | | |
400 | | // syntax mode |
401 | |
|
402 | 0 | if (pOutput->mbSyntaxMode) |
403 | 0 | pOutput->SetSyntaxColor(&aFont, rCell); |
404 | | |
405 | | // There is no cell attribute for kerning, default is kerning OFF, all |
406 | | // kerning is stored at an EditText object that is drawn using EditEngine. |
407 | | // See also matching kerning cases in ScColumn::GetNeededSize and |
408 | | // ScColumn::GetOptimalColWidth. |
409 | 0 | aFont.SetKerning(FontKerning::NONE); |
410 | |
|
411 | 0 | pDev->SetFont( aFont ); |
412 | 0 | if ( pFmtDevice != pDev ) |
413 | 0 | pFmtDevice->SetFont( aFont ); |
414 | |
|
415 | 0 | aMetric = pFmtDevice->GetFontMetric(); |
416 | | |
417 | | // if there is the leading 0 on a printer device, we have problems |
418 | | // -> take metric from the screen (as for EditEngine!) |
419 | 0 | if ( pFmtDevice->GetOutDevType() == OUTDEV_PRINTER && aMetric.GetInternalLeading() == 0 ) |
420 | 0 | { |
421 | 0 | OutputDevice* pDefaultDev = Application::GetDefaultDevice(); |
422 | 0 | MapMode aOld = pDefaultDev->GetMapMode(); |
423 | 0 | pDefaultDev->SetMapMode( pFmtDevice->GetMapMode() ); |
424 | 0 | aMetric = pDefaultDev->GetFontMetric( aFont ); |
425 | 0 | pDefaultDev->SetMapMode( aOld ); |
426 | 0 | } |
427 | |
|
428 | 0 | nAscentPixel = aMetric.GetAscent(); |
429 | 0 | if ( bPixelToLogic ) |
430 | 0 | nAscentPixel = pRefDevice->LogicToPixel( Size( 0, nAscentPixel ) ).Height(); |
431 | |
|
432 | 0 | Color aULineColor( pPattern->GetItem( ATTR_FONT_UNDERLINE, pCondSet ).GetColor() ); |
433 | 0 | pDev->SetTextLineColor( aULineColor ); |
434 | |
|
435 | 0 | Color aOLineColor( pPattern->GetItem( ATTR_FONT_OVERLINE, pCondSet ).GetColor() ); |
436 | 0 | pDev->SetOverlineColor( aOLineColor ); |
437 | | |
438 | | // number format |
439 | |
|
440 | 0 | nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet ); |
441 | | |
442 | | // margins |
443 | 0 | pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet ); |
444 | 0 | if ( eAttrHorJust == SvxCellHorJustify::Left || eAttrHorJust == SvxCellHorJustify::Right ) |
445 | 0 | nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue(); |
446 | 0 | else |
447 | 0 | nIndent = 0; |
448 | | |
449 | | // "Shrink to fit" |
450 | |
|
451 | 0 | bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue(); |
452 | | |
453 | | // at least the text size needs to be retrieved again |
454 | | //! differentiate and do not get the text again from the number format? |
455 | 0 | maLastCell.clear(); |
456 | 0 | } |
457 | | |
458 | | void ScDrawStringsVars::SetPatternSimple( const ScPatternAttr* pNew, const SfxItemSet* pSet ) |
459 | 0 | { |
460 | 0 | nMaxDigitWidth = 0; |
461 | 0 | nSignWidth = 0; |
462 | 0 | nDotWidth = 0; |
463 | 0 | nExpWidth = 0; |
464 | | |
465 | | // Is called, when the font variables do not change (!StringDiffer) |
466 | |
|
467 | 0 | pPattern = pNew; |
468 | 0 | pCondSet = pSet; //! is this needed ??? |
469 | | |
470 | | // number format |
471 | |
|
472 | 0 | sal_uLong nOld = nValueFormat; |
473 | 0 | nValueFormat = pPattern->GetNumberFormat( pOutput->mpDoc->GetFormatTable(), pCondSet ); |
474 | |
|
475 | 0 | if (nValueFormat != nOld) |
476 | 0 | maLastCell.clear(); // always reformat |
477 | | |
478 | | // margins |
479 | |
|
480 | 0 | pMargin = &pPattern->GetItem( ATTR_MARGIN, pCondSet ); |
481 | |
|
482 | 0 | if ( eAttrHorJust == SvxCellHorJustify::Left ) |
483 | 0 | nIndent = pPattern->GetItem( ATTR_INDENT, pCondSet ).GetValue(); |
484 | 0 | else |
485 | 0 | nIndent = 0; |
486 | | |
487 | | // "Shrink to fit" |
488 | |
|
489 | 0 | bShrink = pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue(); |
490 | 0 | } |
491 | | |
492 | | static bool SameValue( const ScRefCellValue& rCell, const ScRefCellValue& rOldCell ) |
493 | 0 | { |
494 | 0 | return rOldCell.getType() == CELLTYPE_VALUE && rCell.getType() == CELLTYPE_VALUE && |
495 | 0 | rCell.getDouble() == rOldCell.getDouble(); |
496 | 0 | } |
497 | | |
498 | | bool ScDrawStringsVars::SetText( const ScRefCellValue& rCell ) |
499 | 0 | { |
500 | 0 | bool bChanged = false; |
501 | |
|
502 | 0 | if (!rCell.isEmpty()) |
503 | 0 | { |
504 | 0 | if (!SameValue(rCell, maLastCell)) |
505 | 0 | { |
506 | 0 | maLastCell = rCell; // store cell |
507 | |
|
508 | 0 | const Color* pColor; |
509 | 0 | sal_uLong nFormat = nValueFormat; |
510 | 0 | aString = ScCellFormat::GetString( rCell, |
511 | 0 | nFormat, &pColor, |
512 | 0 | nullptr, |
513 | 0 | *pOutput->mpDoc, |
514 | 0 | pOutput->mbShowNullValues, |
515 | 0 | pOutput->mbShowFormulas, |
516 | 0 | true ); |
517 | 0 | if ( nFormat ) |
518 | 0 | { |
519 | 0 | nRepeatPos = aString.indexOf( 0x1B ); |
520 | 0 | if ( nRepeatPos != -1 ) |
521 | 0 | { |
522 | 0 | if (nRepeatPos + 1 == aString.getLength()) |
523 | 0 | nRepeatPos = -1; |
524 | 0 | else |
525 | 0 | { |
526 | 0 | nRepeatChar = aString[ nRepeatPos + 1 ]; |
527 | | // delete placeholder and char to repeat |
528 | 0 | aString = aString.replaceAt( nRepeatPos, 2, u"" ); |
529 | | // Do not cache/reuse a repeat-filled string, column |
530 | | // widths or fonts or sizes may differ. |
531 | 0 | maLastCell.clear(); |
532 | 0 | } |
533 | 0 | } |
534 | 0 | } |
535 | 0 | else |
536 | 0 | { |
537 | 0 | nRepeatPos = -1; |
538 | 0 | nRepeatChar = 0x0; |
539 | 0 | } |
540 | 0 | if (aString.getLength() > DRAWTEXT_MAX) |
541 | 0 | aString = aString.copy(0, DRAWTEXT_MAX); |
542 | |
|
543 | 0 | if ( pColor && !pOutput->mbSyntaxMode && !( pOutput->mbUseStyleColor && pOutput->mbForceAutoColor ) ) |
544 | 0 | { |
545 | 0 | OutputDevice* pDev = pOutput->mpDev; |
546 | 0 | aFont.SetColor(*pColor); |
547 | 0 | pDev->SetFont( aFont ); // only for output |
548 | 0 | bChanged = true; |
549 | 0 | maLastCell.clear(); // next time return here again |
550 | 0 | } |
551 | |
|
552 | 0 | TextChanged(); |
553 | 0 | } |
554 | | // otherwise keep string/size |
555 | 0 | } |
556 | 0 | else |
557 | 0 | { |
558 | 0 | aString.clear(); |
559 | 0 | maLastCell.clear(); |
560 | 0 | aTextSize = Size(0,0); |
561 | 0 | nOriginalWidth = 0; |
562 | 0 | } |
563 | |
|
564 | 0 | return bChanged; |
565 | 0 | } |
566 | | |
567 | | void ScDrawStringsVars::SetHashText() |
568 | 0 | { |
569 | 0 | SetAutoText(u"###"_ustr); |
570 | 0 | } |
571 | | |
572 | | void ScDrawStringsVars::RepeatToFill( tools::Long nColWidth ) |
573 | 0 | { |
574 | 0 | if ( nRepeatPos == -1 || nRepeatPos > aString.getLength() ) |
575 | 0 | return; |
576 | | |
577 | | // Measuring a string containing a single copy of the repeat char is inaccurate. |
578 | | // To increase accuracy, start with a representative sample of a padding sequence. |
579 | 0 | constexpr sal_Int32 nSampleSize = 20; |
580 | 0 | OUStringBuffer aFill(nSampleSize); |
581 | 0 | comphelper::string::padToLength(aFill, nSampleSize, nRepeatChar); |
582 | |
|
583 | 0 | tools::Long nSampleWidth = GetFmtTextWidth(aFill.makeStringAndClear()); |
584 | 0 | double nAvgCharWidth = static_cast<double>(nSampleWidth) / static_cast<double>(nSampleSize); |
585 | | |
586 | | // Intentionally truncate to round toward zero |
587 | 0 | auto nCharWidth = static_cast<tools::Long>(nAvgCharWidth); |
588 | 0 | if ( nCharWidth < 1 || (bPixelToLogic && nCharWidth < pOutput->mpRefDevice->PixelToLogic(Size(1,0)).Width()) ) |
589 | 0 | return; |
590 | | |
591 | | // Are there restrictions on the cell type we should filter out here ? |
592 | 0 | tools::Long nTextWidth = aTextSize.Width(); |
593 | 0 | if ( bPixelToLogic ) |
594 | 0 | { |
595 | 0 | nColWidth = pOutput->mpRefDevice->PixelToLogic(Size(nColWidth,0)).Width(); |
596 | 0 | nTextWidth = pOutput->mpRefDevice->PixelToLogic(Size(nTextWidth,0)).Width(); |
597 | 0 | } |
598 | |
|
599 | 0 | tools::Long nSpaceToFill = nColWidth - nTextWidth; |
600 | 0 | if ( nSpaceToFill <= nCharWidth ) |
601 | 0 | return; |
602 | | |
603 | | // Intentionally truncate to round toward zero |
604 | 0 | auto nCharsToInsert = static_cast<sal_Int32>(static_cast<double>(nSpaceToFill) / nAvgCharWidth); |
605 | 0 | aFill.ensureCapacity(nCharsToInsert); |
606 | 0 | comphelper::string::padToLength(aFill, nCharsToInsert, nRepeatChar); |
607 | 0 | aString = aString.replaceAt( nRepeatPos, 0, aFill ); |
608 | 0 | TextChanged(); |
609 | 0 | } |
610 | | |
611 | | bool ScDrawStringsVars::SetTextToWidthOrHash( ScRefCellValue& rCell, tools::Long nWidth ) |
612 | 0 | { |
613 | | // #i113045# do the single-character width calculations in logic units |
614 | 0 | if (bPixelToLogic) |
615 | 0 | nWidth = pOutput->mpRefDevice->PixelToLogic(Size(nWidth,0)).Width(); |
616 | |
|
617 | 0 | CellType eType = rCell.getType(); |
618 | 0 | if (eType != CELLTYPE_VALUE && eType != CELLTYPE_FORMULA) |
619 | | // must be a value or formula cell. |
620 | 0 | return false; |
621 | | |
622 | 0 | if (eType == CELLTYPE_FORMULA) |
623 | 0 | { |
624 | 0 | ScFormulaCell* pFCell = rCell.getFormula(); |
625 | 0 | if (pFCell->GetErrCode() != FormulaError::NONE || pOutput->mbShowFormulas) |
626 | 0 | { |
627 | 0 | SetHashText(); // If the error string doesn't fit, always use "###". Also for "display formulas" (#i116691#) |
628 | 0 | return true; |
629 | 0 | } |
630 | | // If it's formula, the result must be a value. |
631 | 0 | if (!pFCell->IsValue()) |
632 | 0 | return false; |
633 | 0 | } |
634 | | |
635 | 0 | sal_uLong nFormat = GetResultValueFormat(); |
636 | 0 | if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0) |
637 | 0 | { |
638 | | // Not 'General' number format. Set hash text and bail out. |
639 | 0 | SetHashText(); |
640 | 0 | return true; |
641 | 0 | } |
642 | | |
643 | 0 | double fVal = rCell.getValue(); |
644 | |
|
645 | 0 | const SvNumberformat* pNumFormat = pOutput->mpDoc->GetFormatTable()->GetEntry(nFormat); |
646 | 0 | if (!pNumFormat) |
647 | 0 | return false; |
648 | | |
649 | 0 | tools::Long nMaxDigit = GetMaxDigitWidth(); |
650 | 0 | if (!nMaxDigit) |
651 | 0 | return false; |
652 | | |
653 | 0 | sal_uInt16 nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit); |
654 | 0 | { |
655 | 0 | OUString sTempOut(aString); |
656 | 0 | if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut, pOutput->mpDoc->GetFormatTable()->GetNatNum(), |
657 | 0 | pOutput->mpDoc->GetFormatTable()->GetROLanguageData())) |
658 | 0 | { |
659 | 0 | aString = sTempOut; |
660 | | // Failed to get output string. Bail out. |
661 | 0 | return false; |
662 | 0 | } |
663 | 0 | aString = sTempOut; |
664 | 0 | } |
665 | 0 | sal_uInt8 nSignCount = 0, nDecimalCount = 0, nExpCount = 0; |
666 | 0 | sal_Int32 nLen = aString.getLength(); |
667 | 0 | sal_Unicode cDecSep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator[0]; |
668 | 0 | for( sal_Int32 i = 0; i < nLen; ++i ) |
669 | 0 | { |
670 | 0 | sal_Unicode c = aString[i]; |
671 | 0 | if (c == '-') |
672 | 0 | ++nSignCount; |
673 | 0 | else if (c == cDecSep) |
674 | 0 | ++nDecimalCount; |
675 | 0 | else if (c == 'E') |
676 | 0 | ++nExpCount; |
677 | 0 | } |
678 | | |
679 | | // #i112250# A small value might be formatted as "0" when only counting the digits, |
680 | | // but fit into the column when considering the smaller width of the decimal separator. |
681 | 0 | if (aString == "0" && fVal != 0.0) |
682 | 0 | nDecimalCount = 1; |
683 | |
|
684 | 0 | if (nDecimalCount) |
685 | 0 | nWidth += (nMaxDigit - GetDotWidth()) * nDecimalCount; |
686 | 0 | if (nSignCount) |
687 | 0 | nWidth += (nMaxDigit - GetSignWidth()) * nSignCount; |
688 | 0 | if (nExpCount) |
689 | 0 | nWidth += (nMaxDigit - GetExpWidth()) * nExpCount; |
690 | |
|
691 | 0 | if (nDecimalCount || nSignCount || nExpCount) |
692 | 0 | { |
693 | | // Re-calculate. |
694 | 0 | nNumDigits = static_cast<sal_uInt16>(nWidth / nMaxDigit); |
695 | 0 | OUString sTempOut(aString); |
696 | 0 | if (!pNumFormat->GetOutputString(fVal, nNumDigits, sTempOut, pOutput->mpDoc->GetFormatTable()->GetNatNum(), |
697 | 0 | pOutput->mpDoc->GetFormatTable()->GetROLanguageData())) |
698 | 0 | { |
699 | 0 | aString = sTempOut; |
700 | | // Failed to get output string. Bail out. |
701 | 0 | return false; |
702 | 0 | } |
703 | 0 | aString = sTempOut; |
704 | 0 | } |
705 | | |
706 | 0 | tools::Long nActualTextWidth = GetFmtTextWidth(aString); |
707 | 0 | if (nActualTextWidth > nWidth) |
708 | 0 | { |
709 | | // Even after the decimal adjustment the text doesn't fit. Give up. |
710 | 0 | SetHashText(); |
711 | 0 | return true; |
712 | 0 | } |
713 | | |
714 | 0 | TextChanged(); |
715 | 0 | maLastCell.clear(); // #i113022# equal cell and format in another column may give different string |
716 | 0 | return false; |
717 | 0 | } |
718 | | |
719 | | void ScDrawStringsVars::SetAutoText( const OUString& rAutoText ) |
720 | 0 | { |
721 | 0 | aString = rAutoText; |
722 | |
|
723 | 0 | OutputDevice* pRefDevice = pOutput->mpRefDevice; |
724 | 0 | OutputDevice* pFmtDevice = pOutput->pFmtDevice; |
725 | 0 | aTextSize.setWidth( GetFmtTextWidth( aString ) ); |
726 | 0 | aTextSize.setHeight( pFmtDevice->GetTextHeight() ); |
727 | |
|
728 | 0 | if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER ) |
729 | 0 | { |
730 | 0 | double fMul = pOutput->GetStretch(); |
731 | 0 | aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) ); |
732 | 0 | } |
733 | |
|
734 | 0 | aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() ); |
735 | 0 | if ( GetOrient() != SvxCellOrientation::Standard ) |
736 | 0 | { |
737 | 0 | tools::Long nTemp = aTextSize.Height(); |
738 | 0 | aTextSize.setHeight( aTextSize.Width() ); |
739 | 0 | aTextSize.setWidth( nTemp ); |
740 | 0 | } |
741 | |
|
742 | 0 | nOriginalWidth = aTextSize.Width(); |
743 | 0 | if ( bPixelToLogic ) |
744 | 0 | aTextSize = pRefDevice->LogicToPixel( aTextSize ); |
745 | |
|
746 | 0 | maLastCell.clear(); // the same text may fit in the next cell |
747 | 0 | } |
748 | | |
749 | | tools::Long ScDrawStringsVars::GetMaxDigitWidth() |
750 | 0 | { |
751 | 0 | if (nMaxDigitWidth > 0) |
752 | 0 | return nMaxDigitWidth; |
753 | | |
754 | 0 | for (char i = 0; i < 10; ++i) |
755 | 0 | { |
756 | 0 | char cDigit = '0' + i; |
757 | | // Do not cache this with GetFmtTextWidth(), nMaxDigitWidth is already cached. |
758 | 0 | tools::Long n = pOutput->pFmtDevice->GetTextWidth(OUString(cDigit)); |
759 | 0 | nMaxDigitWidth = ::std::max(nMaxDigitWidth, n); |
760 | 0 | } |
761 | 0 | return nMaxDigitWidth; |
762 | 0 | } |
763 | | |
764 | | tools::Long ScDrawStringsVars::GetSignWidth() |
765 | 0 | { |
766 | 0 | if (nSignWidth > 0) |
767 | 0 | return nSignWidth; |
768 | | |
769 | 0 | nSignWidth = pOutput->pFmtDevice->GetTextWidth(OUString('-')); |
770 | 0 | return nSignWidth; |
771 | 0 | } |
772 | | |
773 | | tools::Long ScDrawStringsVars::GetDotWidth() |
774 | 0 | { |
775 | 0 | if (nDotWidth > 0) |
776 | 0 | return nDotWidth; |
777 | | |
778 | 0 | const OUString& sep = ScGlobal::getLocaleData().getLocaleItem().decimalSeparator; |
779 | 0 | nDotWidth = pOutput->pFmtDevice->GetTextWidth(sep); |
780 | 0 | return nDotWidth; |
781 | 0 | } |
782 | | |
783 | | tools::Long ScDrawStringsVars::GetExpWidth() |
784 | 0 | { |
785 | 0 | if (nExpWidth > 0) |
786 | 0 | return nExpWidth; |
787 | | |
788 | 0 | nExpWidth = pOutput->pFmtDevice->GetTextWidth(OUString('E')); |
789 | 0 | return nExpWidth; |
790 | 0 | } |
791 | | |
792 | | tools::Long ScDrawStringsVars::GetFmtTextWidth( const OUString& rString ) |
793 | 0 | { |
794 | 0 | return pOutput->pFmtDevice->GetTextWidth( rString, 0, -1, nullptr, GetLayoutGlyphs( rString )); |
795 | 0 | } |
796 | | |
797 | | void ScDrawStringsVars::TextChanged() |
798 | 0 | { |
799 | 0 | OutputDevice* pRefDevice = pOutput->mpRefDevice; |
800 | 0 | OutputDevice* pFmtDevice = pOutput->pFmtDevice; |
801 | 0 | aTextSize.setWidth( GetFmtTextWidth( aString ) ); |
802 | 0 | aTextSize.setHeight( pFmtDevice->GetTextHeight() ); |
803 | |
|
804 | 0 | if ( !pRefDevice->GetConnectMetaFile() || pRefDevice->GetOutDevType() == OUTDEV_PRINTER ) |
805 | 0 | { |
806 | 0 | double fMul = pOutput->GetStretch(); |
807 | 0 | aTextSize.setWidth( static_cast<tools::Long>(aTextSize.Width() / fMul + 0.5) ); |
808 | 0 | } |
809 | |
|
810 | 0 | aTextSize.setHeight( aMetric.GetAscent() + aMetric.GetDescent() ); |
811 | 0 | if ( GetOrient() != SvxCellOrientation::Standard ) |
812 | 0 | { |
813 | 0 | tools::Long nTemp = aTextSize.Height(); |
814 | 0 | aTextSize.setHeight( aTextSize.Width() ); |
815 | 0 | aTextSize.setWidth( nTemp ); |
816 | 0 | } |
817 | |
|
818 | 0 | nOriginalWidth = aTextSize.Width(); |
819 | 0 | if ( bPixelToLogic ) |
820 | 0 | aTextSize = pRefDevice->LogicToPixel( aTextSize ); |
821 | 0 | } |
822 | | |
823 | | bool ScDrawStringsVars::HasEditCharacters() const |
824 | 0 | { |
825 | 0 | for (sal_Int32 nIdx = 0; nIdx < aString.getLength(); ++nIdx) |
826 | 0 | { |
827 | 0 | switch(aString[nIdx]) |
828 | 0 | { |
829 | 0 | case CHAR_NBSP: |
830 | | // tdf#122676: Ignore CHAR_NBSP (this is thousand separator in any number) |
831 | | // if repeat character is set |
832 | 0 | if (nRepeatPos < 0) |
833 | 0 | return true; |
834 | 0 | break; |
835 | 0 | case CHAR_SHY: |
836 | 0 | case CHAR_ZWSP: |
837 | 0 | case CHAR_LRM: |
838 | 0 | case CHAR_RLM: |
839 | 0 | case CHAR_NBHY: |
840 | 0 | case CHAR_WJ: |
841 | 0 | return true; |
842 | 0 | default: |
843 | 0 | break; |
844 | 0 | } |
845 | 0 | } |
846 | | |
847 | 0 | return false; |
848 | 0 | } |
849 | | |
850 | | double ScOutputData::GetStretch() const |
851 | 0 | { |
852 | 0 | if ( mpRefDevice->IsMapModeEnabled() ) |
853 | 0 | { |
854 | | // If a non-trivial MapMode is set, its scale is now already |
855 | | // taken into account in the OutputDevice's font handling |
856 | | // (OutputDevice::ImplNewFont, see #95414#). |
857 | | // The old handling below is only needed for pixel output. |
858 | 0 | return 1.0; |
859 | 0 | } |
860 | | |
861 | | // calculation in double is faster than Fraction multiplication |
862 | | // and doesn't overflow |
863 | | |
864 | 0 | if ( mpRefDevice == pFmtDevice ) |
865 | 0 | { |
866 | 0 | MapMode aOld = mpRefDevice->GetMapMode(); |
867 | 0 | return aOld.GetScaleY() / aOld.GetScaleX() * mfZoomY / mfZoomX; |
868 | 0 | } |
869 | 0 | else |
870 | 0 | { |
871 | | // when formatting for printer, device map mode has already been taken care of |
872 | 0 | return mfZoomY / mfZoomX; |
873 | 0 | } |
874 | 0 | } |
875 | | |
876 | | // output strings |
877 | | |
878 | | static void lcl_DoHyperlinkResult( const OutputDevice* pDev, const tools::Rectangle& rRect, const ScRefCellValue& rCell ) |
879 | 0 | { |
880 | 0 | vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() ); |
881 | |
|
882 | 0 | OUString aURL; |
883 | 0 | OUString aCellText; |
884 | 0 | if (rCell.getType() == CELLTYPE_FORMULA) |
885 | 0 | { |
886 | 0 | ScFormulaCell* pFCell = rCell.getFormula(); |
887 | 0 | if ( pFCell->IsHyperLinkCell() ) |
888 | 0 | pFCell->GetURLResult( aURL, aCellText ); |
889 | 0 | } |
890 | |
|
891 | 0 | if ( !aURL.isEmpty() && pPDFData ) |
892 | 0 | { |
893 | 0 | vcl::PDFExtOutDevBookmarkEntry aBookmark; |
894 | 0 | aBookmark.nLinkId = pPDFData->CreateLink(rRect, u""_ustr); |
895 | 0 | aBookmark.aBookmark = aURL; |
896 | 0 | std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFData->GetBookmarks(); |
897 | 0 | rBookmarks.push_back( aBookmark ); |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | | void ScOutputData::SetSyntaxColor( vcl::Font* pFont, const ScRefCellValue& rCell ) |
902 | 0 | { |
903 | 0 | switch (rCell.getType()) |
904 | 0 | { |
905 | 0 | case CELLTYPE_VALUE: |
906 | 0 | pFont->SetColor(*mxValueColor); |
907 | 0 | break; |
908 | 0 | case CELLTYPE_STRING: |
909 | 0 | pFont->SetColor(*mxTextColor); |
910 | 0 | break; |
911 | 0 | case CELLTYPE_FORMULA: |
912 | 0 | pFont->SetColor(*mxFormulaColor); |
913 | 0 | break; |
914 | 0 | default: |
915 | 0 | { |
916 | | // added to avoid warnings |
917 | 0 | } |
918 | 0 | } |
919 | 0 | } |
920 | | |
921 | | static void lcl_SetEditColor( EditEngine& rEngine, const Color& rColor ) |
922 | 0 | { |
923 | 0 | ESelection aSel( 0, 0, rEngine.GetParagraphCount(), 0 ); |
924 | 0 | SfxItemSet aSet( rEngine.GetEmptyItemSet() ); |
925 | 0 | aSet.Put( SvxColorItem( rColor, EE_CHAR_COLOR ) ); |
926 | 0 | rEngine.QuickSetAttribs( aSet, aSel ); |
927 | | // function is called with update mode set to FALSE |
928 | 0 | } |
929 | | |
930 | | void ScOutputData::SetEditSyntaxColor( EditEngine& rEngine, const ScRefCellValue& rCell ) |
931 | 0 | { |
932 | 0 | Color aColor; |
933 | 0 | switch (rCell.getType()) |
934 | 0 | { |
935 | 0 | case CELLTYPE_VALUE: |
936 | 0 | aColor = *mxValueColor; |
937 | 0 | break; |
938 | 0 | case CELLTYPE_STRING: |
939 | 0 | case CELLTYPE_EDIT: |
940 | 0 | aColor = *mxTextColor; |
941 | 0 | break; |
942 | 0 | case CELLTYPE_FORMULA: |
943 | 0 | aColor = *mxFormulaColor; |
944 | 0 | break; |
945 | 0 | default: |
946 | 0 | { |
947 | | // added to avoid warnings |
948 | 0 | } |
949 | 0 | } |
950 | 0 | lcl_SetEditColor( rEngine, aColor ); |
951 | 0 | } |
952 | | |
953 | | bool ScOutputData::GetMergeOrigin( SCCOL nX, SCROW nY, SCSIZE nArrY, |
954 | | SCCOL& rOverX, SCROW& rOverY, |
955 | | bool bVisRowChanged ) |
956 | 0 | { |
957 | 0 | bool bDoMerge = false; |
958 | 0 | bool bIsLeft = ( nX == mnVisX1 ); |
959 | 0 | bool bIsTop = ( nY == mnVisY1 ) || bVisRowChanged; |
960 | |
|
961 | 0 | bool bHOver; |
962 | 0 | bool bVOver; |
963 | 0 | bool bHidden; |
964 | |
|
965 | 0 | if (!mpDoc->ColHidden(nX, mnTab) && nX >= mnX1 && nX <= mnX2 |
966 | 0 | && !mpDoc->RowHidden(nY, mnTab) && nY >= mnY1 && nY <= mnY2) |
967 | 0 | { |
968 | 0 | ScCellInfo* pInfo = &mpRowInfo[nArrY].cellInfo(nX); |
969 | 0 | bHOver = pInfo->bHOverlapped; |
970 | 0 | bVOver = pInfo->bVOverlapped; |
971 | 0 | } |
972 | 0 | else |
973 | 0 | { |
974 | 0 | ScMF nOverlap2 = mpDoc->GetAttr(nX, nY, mnTab, ATTR_MERGE_FLAG).GetValue(); |
975 | 0 | bHOver = bool(nOverlap2 & ScMF::Hor); |
976 | 0 | bVOver = bool(nOverlap2 & ScMF::Ver); |
977 | 0 | } |
978 | |
|
979 | 0 | if ( bHOver && bVOver ) |
980 | 0 | bDoMerge = bIsLeft && bIsTop; |
981 | 0 | else if ( bHOver ) |
982 | 0 | bDoMerge = bIsLeft; |
983 | 0 | else if ( bVOver ) |
984 | 0 | bDoMerge = bIsTop; |
985 | |
|
986 | 0 | rOverX = nX; |
987 | 0 | rOverY = nY; |
988 | |
|
989 | 0 | while (bHOver) // nY constant |
990 | 0 | { |
991 | 0 | --rOverX; |
992 | 0 | bHidden = mpDoc->ColHidden(rOverX, mnTab); |
993 | 0 | if ( !bDoMerge && !bHidden ) |
994 | 0 | return false; |
995 | | |
996 | 0 | if (rOverX >= mnX1 && !bHidden) |
997 | 0 | { |
998 | 0 | bHOver = mpRowInfo[nArrY].cellInfo(rOverX).bHOverlapped; |
999 | 0 | bVOver = mpRowInfo[nArrY].cellInfo(rOverX).bVOverlapped; |
1000 | 0 | } |
1001 | 0 | else |
1002 | 0 | { |
1003 | 0 | ScMF nOverlap = mpDoc->GetAttr(rOverX, rOverY, mnTab, ATTR_MERGE_FLAG).GetValue(); |
1004 | 0 | bHOver = bool(nOverlap & ScMF::Hor); |
1005 | 0 | bVOver = bool(nOverlap & ScMF::Ver); |
1006 | 0 | } |
1007 | 0 | } |
1008 | | |
1009 | 0 | while (bVOver) |
1010 | 0 | { |
1011 | 0 | --rOverY; |
1012 | 0 | bHidden = mpDoc->RowHidden(rOverY, mnTab); |
1013 | 0 | if ( !bDoMerge && !bHidden ) |
1014 | 0 | return false; |
1015 | | |
1016 | 0 | if (nArrY>0) |
1017 | 0 | --nArrY; // local copy ! |
1018 | |
|
1019 | 0 | if (rOverX >= mnX1 && rOverY >= mnY1 && |
1020 | 0 | !mpDoc->ColHidden(rOverX, mnTab) && |
1021 | 0 | !mpDoc->RowHidden(rOverY, mnTab) && |
1022 | 0 | mpRowInfo[nArrY].nRowNo == rOverY) |
1023 | 0 | { |
1024 | 0 | bVOver = mpRowInfo[nArrY].cellInfo(rOverX).bVOverlapped; |
1025 | 0 | } |
1026 | 0 | else |
1027 | 0 | { |
1028 | 0 | ScMF nOverlap = mpDoc->GetAttr( rOverX, rOverY, mnTab, ATTR_MERGE_FLAG ).GetValue(); |
1029 | 0 | bVOver = bool(nOverlap & ScMF::Ver); |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | 0 | return true; |
1034 | 0 | } |
1035 | | |
1036 | | static bool StringDiffer( const ScPatternAttr*& rpOldPattern, const ScPatternAttr* pNewPattern ) |
1037 | 0 | { |
1038 | 0 | assert(pNewPattern && "pNewPattern"); |
1039 | |
|
1040 | 0 | if ( ScPatternAttr::areSame( pNewPattern, rpOldPattern ) ) |
1041 | 0 | return false; |
1042 | 0 | else if ( !rpOldPattern ) |
1043 | 0 | return true; |
1044 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT ), rpOldPattern->GetItem( ATTR_FONT ) ) ) |
1045 | 0 | return true; |
1046 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT ), rpOldPattern->GetItem( ATTR_CJK_FONT ) ) ) |
1047 | 0 | return true; |
1048 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT ), rpOldPattern->GetItem( ATTR_CTL_FONT ) ) ) |
1049 | 0 | return true; |
1050 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_FONT_HEIGHT ) ) ) |
1051 | 0 | return true; |
1052 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_HEIGHT ) ) ) |
1053 | 0 | return true; |
1054 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_HEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_HEIGHT ) ) ) |
1055 | 0 | return true; |
1056 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_FONT_WEIGHT ) ) ) |
1057 | 0 | return true; |
1058 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CJK_FONT_WEIGHT ) ) ) |
1059 | 0 | return true; |
1060 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_WEIGHT ), rpOldPattern->GetItem( ATTR_CTL_FONT_WEIGHT ) ) ) |
1061 | 0 | return true; |
1062 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_FONT_POSTURE ) ) ) |
1063 | 0 | return true; |
1064 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CJK_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CJK_FONT_POSTURE ) ) ) |
1065 | 0 | return true; |
1066 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_CTL_FONT_POSTURE ), rpOldPattern->GetItem( ATTR_CTL_FONT_POSTURE ) ) ) |
1067 | 0 | return true; |
1068 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_UNDERLINE ), rpOldPattern->GetItem( ATTR_FONT_UNDERLINE ) ) ) |
1069 | 0 | return true; |
1070 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_OVERLINE ), rpOldPattern->GetItem( ATTR_FONT_OVERLINE ) ) ) |
1071 | 0 | return true; |
1072 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_WORDLINE ), rpOldPattern->GetItem( ATTR_FONT_WORDLINE ) ) ) |
1073 | 0 | return true; |
1074 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CROSSEDOUT ), rpOldPattern->GetItem( ATTR_FONT_CROSSEDOUT ) ) ) |
1075 | 0 | return true; |
1076 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_CONTOUR ), rpOldPattern->GetItem( ATTR_FONT_CONTOUR ) ) ) |
1077 | 0 | return true; |
1078 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_SHADOWED ), rpOldPattern->GetItem( ATTR_FONT_SHADOWED ) ) ) |
1079 | 0 | return true; |
1080 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_COLOR ), rpOldPattern->GetItem( ATTR_FONT_COLOR ) ) ) |
1081 | 0 | return true; |
1082 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY ) ) ) |
1083 | 0 | return true; |
1084 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_HOR_JUSTIFY_METHOD ) ) ) |
1085 | 0 | return true; |
1086 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY ) ) ) |
1087 | 0 | return true; |
1088 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ), rpOldPattern->GetItem( ATTR_VER_JUSTIFY_METHOD ) ) ) |
1089 | 0 | return true; |
1090 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_STACKED ), rpOldPattern->GetItem( ATTR_STACKED ) ) ) |
1091 | 0 | return true; |
1092 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_LINEBREAK ), rpOldPattern->GetItem( ATTR_LINEBREAK ) ) ) |
1093 | 0 | return true; |
1094 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_MARGIN ), rpOldPattern->GetItem( ATTR_MARGIN ) ) ) |
1095 | 0 | return true; |
1096 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_ROTATE_VALUE ), rpOldPattern->GetItem( ATTR_ROTATE_VALUE ) ) ) |
1097 | 0 | return true; |
1098 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FORBIDDEN_RULES ), rpOldPattern->GetItem( ATTR_FORBIDDEN_RULES ) ) ) |
1099 | 0 | return true; |
1100 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_EMPHASISMARK ), rpOldPattern->GetItem( ATTR_FONT_EMPHASISMARK ) ) ) |
1101 | 0 | return true; |
1102 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_FONT_RELIEF ), rpOldPattern->GetItem( ATTR_FONT_RELIEF ) ) ) |
1103 | 0 | return true; |
1104 | 0 | else if ( !SfxPoolItem::areSame( pNewPattern->GetItem( ATTR_BACKGROUND ), rpOldPattern->GetItem( ATTR_BACKGROUND ) ) ) |
1105 | 0 | return true; // needed with automatic text color |
1106 | 0 | else |
1107 | 0 | { |
1108 | 0 | rpOldPattern = pNewPattern; |
1109 | 0 | return false; |
1110 | 0 | } |
1111 | 0 | } |
1112 | | |
1113 | | static void lcl_CreateInterpretProgress( bool& bProgress, ScDocument* pDoc, |
1114 | | const ScFormulaCell* pFCell ) |
1115 | 0 | { |
1116 | 0 | if ( !bProgress && pFCell->GetDirty() ) |
1117 | 0 | { |
1118 | 0 | ScProgress::CreateInterpretProgress( pDoc ); |
1119 | 0 | bProgress = true; |
1120 | 0 | } |
1121 | 0 | } |
1122 | | |
1123 | | static bool IsAmbiguousScript( SvtScriptType nScript ) |
1124 | 0 | { |
1125 | 0 | return ( nScript != SvtScriptType::LATIN && |
1126 | 0 | nScript != SvtScriptType::ASIAN && |
1127 | 0 | nScript != SvtScriptType::COMPLEX ); |
1128 | 0 | } |
1129 | | |
1130 | | bool ScOutputData::IsEmptyCellText( const RowInfo* pThisRowInfo, SCCOL nX, SCROW nY ) |
1131 | 0 | { |
1132 | | // pThisRowInfo may be NULL |
1133 | |
|
1134 | 0 | bool bEmpty; |
1135 | 0 | if ( pThisRowInfo && nX <= mnX2 ) |
1136 | 0 | bEmpty = pThisRowInfo->basicCellInfo(nX).bEmptyCellText; |
1137 | 0 | else |
1138 | 0 | { |
1139 | 0 | ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, mnTab)); |
1140 | 0 | bEmpty = aCell.isEmpty(); |
1141 | 0 | } |
1142 | |
|
1143 | 0 | if ( !bEmpty && ( nX < mnX1 || nX > mnX2 || !pThisRowInfo ) ) |
1144 | 0 | { |
1145 | | // for the range mnX1..mnX2 in RowInfo, cell protection attribute is already evaluated |
1146 | | // into bEmptyCellText in ScDocument::FillInfo / lcl_HidePrint (printfun) |
1147 | |
|
1148 | 0 | bool bIsPrint = ( meType == OUTTYPE_PRINTER ); |
1149 | |
|
1150 | 0 | if ( bIsPrint || mbTabProtected ) |
1151 | 0 | { |
1152 | 0 | const ScProtectionAttr* pAttr = |
1153 | 0 | mpDoc->GetEffItem( nX, nY, mnTab, ATTR_PROTECTION ); |
1154 | 0 | if ( bIsPrint && pAttr->GetHidePrint() ) |
1155 | 0 | bEmpty = true; |
1156 | 0 | else if ( mbTabProtected ) |
1157 | 0 | { |
1158 | 0 | if ( pAttr->GetHideCell() ) |
1159 | 0 | bEmpty = true; |
1160 | 0 | else if ( mbShowFormulas && pAttr->GetHideFormula() ) |
1161 | 0 | { |
1162 | 0 | if (mpDoc->GetCellType(ScAddress(nX, nY, mnTab)) == CELLTYPE_FORMULA) |
1163 | 0 | bEmpty = true; |
1164 | 0 | } |
1165 | 0 | } |
1166 | 0 | } |
1167 | 0 | } |
1168 | 0 | return bEmpty; |
1169 | 0 | } |
1170 | | |
1171 | | void ScOutputData::GetVisibleCell( SCCOL nCol, SCROW nRow, SCTAB nTabP, ScRefCellValue& rCell ) |
1172 | 0 | { |
1173 | 0 | rCell.assign(*mpDoc, ScAddress(nCol, nRow, nTabP)); |
1174 | 0 | if (!rCell.isEmpty() && IsEmptyCellText(nullptr, nCol, nRow)) |
1175 | 0 | rCell.clear(); |
1176 | 0 | } |
1177 | | |
1178 | | bool ScOutputData::IsAvailable( SCCOL nX, SCROW nY ) |
1179 | 0 | { |
1180 | | // apply the same logic here as in DrawStrings/DrawEdit: |
1181 | | // Stop at non-empty or merged or overlapped cell, |
1182 | | // where a note is empty as well as a cell that's hidden by protection settings |
1183 | |
|
1184 | 0 | ScRefCellValue aCell(*mpDoc, ScAddress(nX, nY, mnTab)); |
1185 | 0 | if (!aCell.isEmpty() && !IsEmptyCellText(nullptr, nX, nY)) |
1186 | 0 | return false; |
1187 | | |
1188 | 0 | const ScPatternAttr* pPattern = mpDoc->GetPattern( nX, nY, mnTab ); |
1189 | 0 | return !(pPattern->GetItem(ATTR_MERGE).IsMerged() || |
1190 | 0 | pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped()); |
1191 | 0 | } |
1192 | | |
1193 | | // nX, nArrY: loop variables from DrawStrings / DrawEdit |
1194 | | // nPosX, nPosY: corresponding positions for nX, nArrY |
1195 | | // nCellX, nCellY: position of the cell that contains the text |
1196 | | // nNeeded: Text width, including margin |
1197 | | // rPattern: cell format at nCellX, nCellY |
1198 | | // nHorJustify: horizontal alignment (visual) to determine which cells to use for long strings |
1199 | | // bCellIsValue: if set, don't extend into empty cells |
1200 | | // bBreak: if set, don't extend, and don't set clip marks (but rLeftClip/rRightClip is set) |
1201 | | // bOverwrite: if set, also extend into non-empty cells (for rotated text) |
1202 | | // rParam output: various area parameters. |
1203 | | |
1204 | | void ScOutputData::GetOutputArea( SCCOL nX, SCSIZE nArrY, tools::Long nPosX, tools::Long nPosY, |
1205 | | SCCOL nCellX, SCROW nCellY, tools::Long nNeeded, |
1206 | | const ScPatternAttr& rPattern, |
1207 | | sal_uInt16 nHorJustify, bool bCellIsValue, |
1208 | | bool bBreak, bool bOverwrite, |
1209 | | OutputAreaParam& rParam ) |
1210 | 0 | { |
1211 | | // rThisRowInfo may be for a different row than nCellY, is still used for clip marks |
1212 | 0 | RowInfo& rThisRowInfo = mpRowInfo[nArrY]; |
1213 | |
|
1214 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
1215 | |
|
1216 | 0 | tools::Long nCellPosX = nPosX; // find nCellX position, starting at nX/nPosX |
1217 | 0 | SCCOL nCompCol = nX; |
1218 | 0 | while ( nCellX > nCompCol ) |
1219 | 0 | { |
1220 | | //! extra member function for width? |
1221 | 0 | tools::Long nColWidth = ( nCompCol <= mnX2 ) ? |
1222 | 0 | mpRowInfo[0].basicCellInfo(nCompCol).nWidth : |
1223 | 0 | static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, mnTab ) * mnPPTX ); |
1224 | 0 | nCellPosX += nColWidth * nLayoutSign; |
1225 | 0 | ++nCompCol; |
1226 | 0 | } |
1227 | 0 | while ( nCellX < nCompCol ) |
1228 | 0 | { |
1229 | 0 | --nCompCol; |
1230 | 0 | tools::Long nColWidth = ( nCompCol <= mnX2 ) ? |
1231 | 0 | mpRowInfo[0].basicCellInfo(nCompCol).nWidth : |
1232 | 0 | static_cast<tools::Long>( mpDoc->GetColWidth( nCompCol, mnTab ) * mnPPTX ); |
1233 | 0 | nCellPosX -= nColWidth * nLayoutSign; |
1234 | 0 | } |
1235 | |
|
1236 | 0 | tools::Long nCellPosY = nPosY; // find nCellY position, starting at nArrY/nPosY |
1237 | 0 | SCSIZE nCompArr = nArrY; |
1238 | 0 | SCROW nCompRow = mpRowInfo[nCompArr].nRowNo; |
1239 | 0 | while ( nCellY > nCompRow ) |
1240 | 0 | { |
1241 | 0 | if ( nCompArr + 1 < mnArrCount ) |
1242 | 0 | { |
1243 | 0 | nCellPosY += mpRowInfo[nCompArr].nHeight; |
1244 | 0 | ++nCompArr; |
1245 | 0 | nCompRow = mpRowInfo[nCompArr].nRowNo; |
1246 | 0 | } |
1247 | 0 | else |
1248 | 0 | { |
1249 | 0 | sal_uInt16 nDocHeight = mpDoc->GetRowHeight( nCompRow, mnTab ); |
1250 | 0 | if ( nDocHeight ) |
1251 | 0 | nCellPosY += static_cast<tools::Long>( nDocHeight * mnPPTY ); |
1252 | 0 | ++nCompRow; |
1253 | 0 | } |
1254 | 0 | } |
1255 | 0 | nCellPosY -= mpDoc->GetScaledRowHeight( nCellY, nCompRow-1, mnTab, mnPPTY ); |
1256 | |
|
1257 | 0 | const ScMergeAttr* pMerge = &rPattern.GetItem( ATTR_MERGE ); |
1258 | 0 | bool bMerged = pMerge->IsMerged(); |
1259 | 0 | tools::Long nMergeCols = pMerge->GetColMerge(); |
1260 | 0 | if ( nMergeCols == 0 ) |
1261 | 0 | nMergeCols = 1; |
1262 | 0 | tools::Long nMergeRows = pMerge->GetRowMerge(); |
1263 | 0 | if ( nMergeRows == 0 ) |
1264 | 0 | nMergeRows = 1; |
1265 | |
|
1266 | 0 | tools::Long nMergeSizeX = 0; |
1267 | 0 | for ( tools::Long i=0; i<nMergeCols; i++ ) |
1268 | 0 | { |
1269 | 0 | tools::Long nColWidth = ( nCellX+i <= mnX2 ) ? |
1270 | 0 | mpRowInfo[0].basicCellInfo(nCellX+i).nWidth : |
1271 | 0 | static_cast<tools::Long>( mpDoc->GetColWidth( sal::static_int_cast<SCCOL>(nCellX+i), mnTab ) * mnPPTX ); |
1272 | 0 | nMergeSizeX += nColWidth; |
1273 | 0 | } |
1274 | 0 | tools::Long nMergeSizeY = 0; |
1275 | 0 | short nDirect = 0; |
1276 | 0 | if ( rThisRowInfo.nRowNo == nCellY ) |
1277 | 0 | { |
1278 | | // take first row's height from row info |
1279 | 0 | nMergeSizeY += rThisRowInfo.nHeight; |
1280 | 0 | nDirect = 1; // skip in loop |
1281 | 0 | } |
1282 | | // following rows always from document |
1283 | 0 | nMergeSizeY += mpDoc->GetScaledRowHeight( nCellY+nDirect, nCellY+nMergeRows-1, mnTab, mnPPTY); |
1284 | |
|
1285 | 0 | --nMergeSizeX; // leave out the grid horizontally, also for alignment (align between grid lines) |
1286 | |
|
1287 | 0 | rParam.mnColWidth = nMergeSizeX; // store the actual column width. |
1288 | 0 | rParam.mnLeftClipLength = rParam.mnRightClipLength = 0; |
1289 | | |
1290 | | // construct the rectangles using logical left/right values (justify is called at the end) |
1291 | | |
1292 | | // rAlignRect is the single cell or merged area, used for alignment. |
1293 | |
|
1294 | 0 | rParam.maAlignRect.SetLeft( nCellPosX ); |
1295 | 0 | rParam.maAlignRect.SetRight( nCellPosX + ( nMergeSizeX - 1 ) * nLayoutSign ); |
1296 | 0 | rParam.maAlignRect.SetTop( nCellPosY ); |
1297 | 0 | rParam.maAlignRect.SetBottom( nCellPosY + nMergeSizeY - 1 ); |
1298 | | |
1299 | | // rClipRect is all cells that are used for output. |
1300 | | // For merged cells this is the same as rAlignRect, otherwise neighboring cells can also be used. |
1301 | |
|
1302 | 0 | rParam.maClipRect = rParam.maAlignRect; |
1303 | 0 | if ( nNeeded > nMergeSizeX ) |
1304 | 0 | { |
1305 | 0 | SvxCellHorJustify eHorJust = static_cast<SvxCellHorJustify>(nHorJustify); |
1306 | |
|
1307 | 0 | tools::Long nMissing = nNeeded - nMergeSizeX; |
1308 | 0 | tools::Long nLeftMissing = 0; |
1309 | 0 | tools::Long nRightMissing = 0; |
1310 | 0 | switch ( eHorJust ) |
1311 | 0 | { |
1312 | 0 | case SvxCellHorJustify::Left: |
1313 | 0 | nRightMissing = nMissing; |
1314 | 0 | break; |
1315 | 0 | case SvxCellHorJustify::Right: |
1316 | 0 | nLeftMissing = nMissing; |
1317 | 0 | break; |
1318 | 0 | case SvxCellHorJustify::Center: |
1319 | 0 | nLeftMissing = nMissing / 2; |
1320 | 0 | nRightMissing = nMissing - nLeftMissing; |
1321 | 0 | break; |
1322 | 0 | default: |
1323 | 0 | { |
1324 | | // added to avoid warnings |
1325 | 0 | } |
1326 | 0 | } |
1327 | | |
1328 | | // nLeftMissing, nRightMissing are logical, eHorJust values are visual |
1329 | 0 | if ( mbLayoutRTL ) |
1330 | 0 | ::std::swap( nLeftMissing, nRightMissing ); |
1331 | |
|
1332 | 0 | SCCOL nRightX = nCellX; |
1333 | 0 | SCCOL nLeftX = nCellX; |
1334 | 0 | if ( !bMerged && !bCellIsValue && !bBreak ) |
1335 | 0 | { |
1336 | | // look for empty cells into which the text can be extended |
1337 | |
|
1338 | 0 | while ( nRightMissing > 0 && nRightX < mpDoc->MaxCol() && ( bOverwrite || IsAvailable( nRightX+1, nCellY ) ) ) |
1339 | 0 | { |
1340 | 0 | ++nRightX; |
1341 | 0 | tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nRightX, mnTab ) * mnPPTX ); |
1342 | 0 | nRightMissing -= nAdd; |
1343 | 0 | rParam.maClipRect.AdjustRight(nAdd * nLayoutSign ); |
1344 | |
|
1345 | 0 | if ( rThisRowInfo.nRowNo == nCellY && nRightX >= mnX1 && nRightX <= mnX2 ) |
1346 | 0 | rThisRowInfo.cellInfo(nRightX-1).bHideGrid = true; |
1347 | 0 | } |
1348 | |
|
1349 | 0 | while ( nLeftMissing > 0 && nLeftX > 0 && ( bOverwrite || IsAvailable( nLeftX-1, nCellY ) ) ) |
1350 | 0 | { |
1351 | 0 | if ( rThisRowInfo.nRowNo == nCellY && nLeftX >= mnX1 && nLeftX <= mnX2 ) |
1352 | 0 | rThisRowInfo.cellInfo(nLeftX-1).bHideGrid = true; |
1353 | |
|
1354 | 0 | --nLeftX; |
1355 | 0 | tools::Long nAdd = static_cast<tools::Long>( mpDoc->GetColWidth( nLeftX, mnTab ) * mnPPTX ); |
1356 | 0 | nLeftMissing -= nAdd; |
1357 | 0 | rParam.maClipRect.AdjustLeft( -(nAdd * nLayoutSign) ); |
1358 | 0 | } |
1359 | 0 | } |
1360 | | |
1361 | | // Set flag and reserve space for clipping mark triangle, |
1362 | | // even if rThisRowInfo isn't for nCellY (merged cells). |
1363 | 0 | if ( nRightMissing > 0 && mbMarkClipped && nRightX >= mnX1 && nRightX <= mnX2 && !bBreak && !bCellIsValue ) |
1364 | 0 | { |
1365 | 0 | rThisRowInfo.cellInfo(nRightX).nClipMark |= ScClipMark::Right; |
1366 | 0 | mbAnyClipped = true; |
1367 | 0 | tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX ); |
1368 | 0 | rParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); |
1369 | 0 | } |
1370 | 0 | if ( nLeftMissing > 0 && mbMarkClipped && nLeftX >= mnX1 && nLeftX <= mnX2 && !bBreak && !bCellIsValue ) |
1371 | 0 | { |
1372 | 0 | rThisRowInfo.cellInfo(nLeftX).nClipMark |= ScClipMark::Left; |
1373 | 0 | mbAnyClipped = true; |
1374 | 0 | tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX ); |
1375 | 0 | rParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign ); |
1376 | 0 | } |
1377 | |
|
1378 | 0 | rParam.mbLeftClip = ( nLeftMissing > 0 ); |
1379 | 0 | rParam.mbRightClip = ( nRightMissing > 0 ); |
1380 | 0 | rParam.mnLeftClipLength = nLeftMissing; |
1381 | 0 | rParam.mnRightClipLength = nRightMissing; |
1382 | 0 | } |
1383 | 0 | else |
1384 | 0 | { |
1385 | 0 | rParam.mbLeftClip = rParam.mbRightClip = false; |
1386 | | |
1387 | | // leave space for AutoFilter on screen |
1388 | | // (for automatic line break: only if not formatting for printer, as in ScColumn::GetNeededSize) |
1389 | |
|
1390 | 0 | if ( meType==OUTTYPE_WINDOW && |
1391 | 0 | ( rPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & (ScMF::Auto|ScMF::Button|ScMF::ButtonPopup) ) && |
1392 | 0 | ( !bBreak || mpRefDevice == pFmtDevice ) ) |
1393 | 0 | { |
1394 | | // filter drop-down width depends on row height |
1395 | 0 | double fZoom = mpRefDevice ? mpRefDevice->GetMapMode().GetScaleY() : 1.0; |
1396 | 0 | fZoom = fZoom > 1.0 ? fZoom : 1.0; |
1397 | 0 | const tools::Long nFilter = fZoom * DROPDOWN_BITMAP_SIZE; |
1398 | 0 | bool bFit = ( nNeeded + nFilter <= nMergeSizeX ); |
1399 | 0 | if ( bFit ) |
1400 | 0 | { |
1401 | | // content fits even in the remaining area without the filter button |
1402 | | // -> align within that remaining area |
1403 | |
|
1404 | 0 | rParam.maAlignRect.AdjustRight( -(nFilter * nLayoutSign) ); |
1405 | 0 | rParam.maClipRect.AdjustRight( -(nFilter * nLayoutSign) ); |
1406 | 0 | } |
1407 | 0 | } |
1408 | 0 | } |
1409 | | |
1410 | | // justify both rectangles for alignment calculation, use with DrawText etc. |
1411 | | |
1412 | 0 | rParam.maAlignRect.Normalize(); |
1413 | 0 | rParam.maClipRect.Normalize(); |
1414 | 0 | } |
1415 | | |
1416 | | namespace { |
1417 | | |
1418 | | bool beginsWithRTLCharacter(const OUString& rStr) |
1419 | 0 | { |
1420 | 0 | if (rStr.isEmpty()) |
1421 | 0 | return false; |
1422 | | |
1423 | 0 | switch (ScGlobal::getCharClass().getCharacterDirection(rStr, 0)) |
1424 | 0 | { |
1425 | 0 | case i18n::DirectionProperty_RIGHT_TO_LEFT: |
1426 | 0 | case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC: |
1427 | 0 | case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING: |
1428 | 0 | case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE: |
1429 | 0 | return true; |
1430 | 0 | default: |
1431 | 0 | ; |
1432 | 0 | } |
1433 | | |
1434 | 0 | return false; |
1435 | 0 | } |
1436 | | |
1437 | | } |
1438 | | |
1439 | | /** Get left, right or centered alignment from RTL context. |
1440 | | |
1441 | | Does not return standard, block or repeat, for these the contextual left or |
1442 | | right alignment is returned. |
1443 | | */ |
1444 | | static SvxCellHorJustify getAlignmentFromContext( SvxCellHorJustify eInHorJust, |
1445 | | bool bCellIsValue, const OUString& rText, |
1446 | | const ScPatternAttr& rPattern, const SfxItemSet* pCondSet, |
1447 | | const ScDocument* pDoc, SCTAB mnTab, const bool bNumberFormatIsText ) |
1448 | 0 | { |
1449 | 0 | SvxCellHorJustify eHorJustContext = eInHorJust; |
1450 | 0 | bool bUseWritingDirection = false; |
1451 | 0 | if (eInHorJust == SvxCellHorJustify::Standard) |
1452 | 0 | { |
1453 | | // fdo#32530: Default alignment depends on value vs |
1454 | | // string, and the direction of the 1st letter. |
1455 | 0 | if (beginsWithRTLCharacter( rText)) //If language is RTL |
1456 | 0 | { |
1457 | 0 | if (bCellIsValue) |
1458 | 0 | eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Right : SvxCellHorJustify::Left; |
1459 | 0 | else |
1460 | 0 | eHorJustContext = SvxCellHorJustify::Right; |
1461 | 0 | } |
1462 | 0 | else if (bCellIsValue) //If language is not RTL |
1463 | 0 | eHorJustContext = bNumberFormatIsText ? SvxCellHorJustify::Left : SvxCellHorJustify::Right; |
1464 | 0 | else |
1465 | 0 | bUseWritingDirection = true; |
1466 | 0 | } |
1467 | |
|
1468 | 0 | if (bUseWritingDirection || |
1469 | 0 | eInHorJust == SvxCellHorJustify::Block || eInHorJust == SvxCellHorJustify::Repeat) |
1470 | 0 | { |
1471 | 0 | SvxFrameDirection nDirection = lcl_GetValue<SvxFrameDirectionItem, SvxFrameDirection>(rPattern, ATTR_WRITINGDIR, pCondSet); |
1472 | 0 | if (nDirection == SvxFrameDirection::Horizontal_LR_TB || nDirection == SvxFrameDirection::Vertical_LR_TB) |
1473 | 0 | eHorJustContext = SvxCellHorJustify::Left; |
1474 | 0 | else if (nDirection == SvxFrameDirection::Environment) |
1475 | 0 | { |
1476 | 0 | SAL_WARN_IF( !pDoc, "sc.ui", "getAlignmentFromContext - pDoc==NULL"); |
1477 | | // fdo#73588: The content of the cell must also |
1478 | | // begin with a RTL character to be right |
1479 | | // aligned; otherwise, it should be left aligned. |
1480 | 0 | eHorJustContext = (pDoc && pDoc->IsLayoutRTL(mnTab) && (beginsWithRTLCharacter( rText))) ? SvxCellHorJustify::Right : SvxCellHorJustify::Left; |
1481 | 0 | } |
1482 | 0 | else |
1483 | 0 | eHorJustContext = SvxCellHorJustify::Right; |
1484 | 0 | } |
1485 | 0 | return eHorJustContext; |
1486 | 0 | } |
1487 | | |
1488 | | void ScOutputData::DrawStrings( bool bPixelToLogic ) |
1489 | 0 | { |
1490 | 0 | LayoutStrings(bPixelToLogic); |
1491 | 0 | } |
1492 | | |
1493 | | void ScOutputData::LayoutStrings(bool bPixelToLogic) |
1494 | 0 | { |
1495 | 0 | vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData()); |
1496 | 0 | bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF(); |
1497 | 0 | if (bTaggedPDF) |
1498 | 0 | { |
1499 | 0 | pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::Table, u"Table"_ustr); |
1500 | 0 | pPDF->GetScPDFState()->m_TableRowMap.clear(); |
1501 | 0 | } |
1502 | |
|
1503 | 0 | bool bOrigIsInLayoutStrings = mpDoc->IsInLayoutStrings(); |
1504 | 0 | mpDoc->SetLayoutStrings(true); |
1505 | 0 | OSL_ENSURE( mpDev == mpRefDevice || |
1506 | 0 | mpDev->GetMapMode().GetMapUnit() == mpRefDevice->GetMapMode().GetMapUnit(), |
1507 | 0 | "LayoutStrings: different MapUnits ?!?!" ); |
1508 | 0 | vcl::text::ComplexTextLayoutFlags eTextLayout = mpDev->GetLayoutMode(); |
1509 | 0 | mpDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default); |
1510 | |
|
1511 | 0 | comphelper::ScopeGuard g([this, bOrigIsInLayoutStrings, eTextLayout] { |
1512 | 0 | mpDoc->SetLayoutStrings(bOrigIsInLayoutStrings); |
1513 | 0 | mpDev->SetLayoutMode(eTextLayout); |
1514 | 0 | }); |
1515 | |
|
1516 | 0 | sc::IdleSwitch aIdleSwitch(*mpDoc, false); |
1517 | | |
1518 | | // Try to limit interpreting to only visible cells. Calling e.g. IsValue() |
1519 | | // on a formula cell that needs interpreting would call Interpret() |
1520 | | // for the entire formula group, which could be large. |
1521 | 0 | mpDoc->InterpretCellsIfNeeded( ScRange( mnX1, mnY1, mnTab, mnX2, mnY2, mnTab )); |
1522 | |
|
1523 | 0 | ScDrawStringsVars aVars( this, bPixelToLogic ); |
1524 | |
|
1525 | 0 | bool bProgress = false; |
1526 | |
|
1527 | 0 | tools::Long nInitPosX = mnScrX; |
1528 | 0 | if ( mbLayoutRTL ) |
1529 | 0 | nInitPosX += mnMirrorW - 1; // pixels |
1530 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
1531 | |
|
1532 | 0 | SCCOL nLastContentCol = mpDoc->MaxCol(); |
1533 | 0 | if ( mnX2 < mpDoc->MaxCol() ) |
1534 | 0 | { |
1535 | 0 | SCROW nEndRow; |
1536 | 0 | mpDoc->GetCellArea(mnTab, nLastContentCol, nEndRow); |
1537 | 0 | } |
1538 | |
|
1539 | 0 | SCCOL nLoopStartX = mnX1; |
1540 | 0 | if ( mnX1 > 0 && !bTaggedPDF ) |
1541 | 0 | --nLoopStartX; // start before mnX1 for rest of long text to the left |
1542 | | |
1543 | | // variables for GetOutputArea |
1544 | 0 | const ScPatternAttr* pOldPattern = nullptr; |
1545 | 0 | const SfxItemSet* pOldCondSet = nullptr; |
1546 | 0 | const SfxItemSet* pOldTableSet = nullptr; |
1547 | 0 | SvtScriptType nOldScript = SvtScriptType::NONE; |
1548 | | |
1549 | | // alternative pattern instances in case we need to modify the pattern |
1550 | | // before processing the cell value. |
1551 | 0 | std::vector<std::unique_ptr<ScPatternAttr> > aAltPatterns; |
1552 | |
|
1553 | 0 | KernArray aDX; |
1554 | 0 | tools::Long nPosY = mnScrY; |
1555 | 0 | for (SCSIZE nArrY=1; nArrY+1<mnArrCount; nArrY++) |
1556 | 0 | { |
1557 | 0 | RowInfo* pThisRowInfo = &mpRowInfo[nArrY]; |
1558 | 0 | SCROW nY = pThisRowInfo->nRowNo; |
1559 | 0 | if (pThisRowInfo->bChanged) |
1560 | 0 | { |
1561 | 0 | bool bReopenRowTag = false; |
1562 | 0 | tools::Long nPosX = nInitPosX; |
1563 | 0 | if ( nLoopStartX < mnX1 ) |
1564 | 0 | nPosX -= mpRowInfo[0].basicCellInfo(nLoopStartX).nWidth * nLayoutSign; |
1565 | 0 | std::optional<SCCOL> oFirstNonEmptyCellX; |
1566 | 0 | std::optional<SCCOL> oLastEmptyCellX; |
1567 | 0 | for (SCCOL nX=nLoopStartX; nX<=mnX2; nX++) |
1568 | 0 | { |
1569 | 0 | LayoutStringsImpl(bPixelToLogic, pThisRowInfo, nX, nY, nArrY, oFirstNonEmptyCellX, oLastEmptyCellX, nLastContentCol, |
1570 | 0 | aAltPatterns, pOldPattern, pOldCondSet, pOldTableSet, nOldScript, aVars, |
1571 | 0 | bProgress, nPosX, nPosY, bTaggedPDF, bReopenRowTag, pPDF, nLayoutSign, aDX); |
1572 | 0 | nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign; |
1573 | 0 | } |
1574 | 0 | } |
1575 | 0 | nPosY += mpRowInfo[nArrY].nHeight; |
1576 | 0 | } |
1577 | 0 | if (bTaggedPDF) |
1578 | 0 | pPDF->EndStructureElement(); // Table |
1579 | |
|
1580 | 0 | if ( bProgress ) |
1581 | 0 | ScProgress::DeleteInterpretProgress(); |
1582 | 0 | } |
1583 | | |
1584 | | /// inner loop of LayoutStrings |
1585 | | void ScOutputData::LayoutStringsImpl(bool const bPixelToLogic, RowInfo* const pThisRowInfo, |
1586 | | SCCOL const nX, SCROW const nY, SCSIZE const nArrY, |
1587 | | std::optional<SCCOL>& oFirstNonEmptyCellX, |
1588 | | std::optional<SCCOL>& oLastEmptyCellX, |
1589 | | SCCOL const nLastContentCol, |
1590 | | std::vector<std::unique_ptr<ScPatternAttr> >& aAltPatterns, |
1591 | | const ScPatternAttr*& pOldPattern, |
1592 | | const SfxItemSet*& pOldCondSet, |
1593 | | const SfxItemSet*& pOldTableSet, |
1594 | | SvtScriptType& nOldScript, |
1595 | | ScDrawStringsVars& aVars, |
1596 | | bool& bProgress, tools::Long const nPosX, tools::Long const nPosY, |
1597 | | bool const bTaggedPDF, bool& bReopenRowTag, vcl::PDFExtOutDevData* const pPDF, |
1598 | | tools::Long const nLayoutSign, KernArray& aDX) |
1599 | 0 | { |
1600 | 0 | const ScPatternAttr* pPattern = nullptr; |
1601 | 0 | const SfxItemSet* pCondSet = nullptr; |
1602 | 0 | const SfxItemSet* pTableSet = nullptr; |
1603 | 0 | bool bCellIsValue = false; |
1604 | 0 | tools::Long nNeededWidth = 0; |
1605 | 0 | OutputAreaParam aAreaParam; |
1606 | 0 | bool bMergeEmpty = false; |
1607 | 0 | const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX); |
1608 | 0 | bool bEmpty = nX < mnX1 || pThisRowInfo->basicCellInfo(nX).bEmptyCellText; |
1609 | |
|
1610 | 0 | SCCOL nCellX = nX; // position where the cell really starts |
1611 | 0 | SCROW nCellY = nY; |
1612 | 0 | bool bDoCell = false; |
1613 | 0 | bool bUseEditEngine = false; |
1614 | | |
1615 | | // Part of a merged cell? |
1616 | |
|
1617 | 0 | bool bOverlapped = (pInfo->bHOverlapped || pInfo->bVOverlapped); |
1618 | 0 | if ( bOverlapped ) |
1619 | 0 | { |
1620 | 0 | bEmpty = true; |
1621 | |
|
1622 | 0 | SCCOL nOverX; // start of the merged cells |
1623 | 0 | SCROW nOverY; |
1624 | 0 | bool bVisChanged = !mpRowInfo[nArrY-1].bChanged; |
1625 | 0 | if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, bVisChanged )) |
1626 | 0 | { |
1627 | 0 | nCellX = nOverX; |
1628 | 0 | nCellY = nOverY; |
1629 | 0 | bDoCell = true; |
1630 | 0 | } |
1631 | 0 | else |
1632 | 0 | bMergeEmpty = true; |
1633 | 0 | } |
1634 | | |
1635 | | // Is there a long text further to the left, that overlaps this region? |
1636 | |
|
1637 | 0 | if ( bEmpty && !bMergeEmpty && nX < mnX1 && !bOverlapped ) |
1638 | 0 | { |
1639 | 0 | if (!oFirstNonEmptyCellX) |
1640 | 0 | { |
1641 | 0 | SCCOL nTempX=mnX1; |
1642 | 0 | while (nTempX > 0 && IsEmptyCellText( pThisRowInfo, nTempX, nY )) |
1643 | 0 | --nTempX; |
1644 | 0 | oFirstNonEmptyCellX = nTempX; |
1645 | 0 | } |
1646 | |
|
1647 | 0 | if ( *oFirstNonEmptyCellX < mnX1 && |
1648 | 0 | !IsEmptyCellText( pThisRowInfo, *oFirstNonEmptyCellX, nY ) && |
1649 | 0 | !mpDoc->HasAttrib( *oFirstNonEmptyCellX,nY,mnTab, mnX1,nY,mnTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) |
1650 | 0 | { |
1651 | 0 | nCellX = *oFirstNonEmptyCellX; |
1652 | 0 | bDoCell = true; |
1653 | 0 | } |
1654 | 0 | } |
1655 | | |
1656 | | // Is there a long text further to the right, that overlaps this region? |
1657 | |
|
1658 | 0 | if ( bEmpty && !bMergeEmpty && nX == mnX2 && !bOverlapped ) |
1659 | 0 | { |
1660 | 0 | if (!oLastEmptyCellX) |
1661 | 0 | { |
1662 | | // don't have to look further than nLastContentCol |
1663 | 0 | SCCOL nTempX=nX; |
1664 | 0 | while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) |
1665 | 0 | ++nTempX; |
1666 | 0 | oLastEmptyCellX = nTempX; |
1667 | 0 | } |
1668 | |
|
1669 | 0 | if ( *oLastEmptyCellX > nX && |
1670 | 0 | !IsEmptyCellText( pThisRowInfo, *oLastEmptyCellX, nY ) && |
1671 | 0 | !mpDoc->HasAttrib( *oLastEmptyCellX, nY,mnTab, nX,nY,mnTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) |
1672 | 0 | { |
1673 | 0 | nCellX = *oLastEmptyCellX; |
1674 | 0 | bDoCell = true; |
1675 | 0 | } |
1676 | 0 | } |
1677 | | |
1678 | | // normal visible cell |
1679 | |
|
1680 | 0 | if (!bEmpty) |
1681 | 0 | bDoCell = true; |
1682 | | |
1683 | | // don't output the cell that's being edited |
1684 | |
|
1685 | 0 | if ( bDoCell && mbEditMode && nCellX == mnEditCol && nCellY == mnEditRow ) |
1686 | 0 | bDoCell = false; |
1687 | | |
1688 | | // skip text in cell if data bar/icon set is set and only value selected |
1689 | 0 | if ( bDoCell ) |
1690 | 0 | { |
1691 | 0 | if(pInfo->pDataBar && !pInfo->pDataBar->mbShowValue) |
1692 | 0 | bDoCell = false; |
1693 | 0 | if(pInfo->pIconSet && !pInfo->pIconSet->mbShowValue) |
1694 | 0 | bDoCell = false; |
1695 | 0 | } |
1696 | | |
1697 | | // output the cell text |
1698 | |
|
1699 | 0 | ScRefCellValue aCell; |
1700 | 0 | if (bDoCell) |
1701 | 0 | { |
1702 | 0 | if ( nCellY == nY && nCellX == nX && nCellX >= mnX1 && nCellX <= mnX2 ) |
1703 | 0 | aCell = pThisRowInfo->cellInfo(nCellX).maCell; |
1704 | 0 | else |
1705 | 0 | GetVisibleCell( nCellX, nCellY, mnTab, aCell ); // get from document |
1706 | 0 | if (aCell.isEmpty()) |
1707 | 0 | bDoCell = false; |
1708 | 0 | else if (aCell.getType() == CELLTYPE_EDIT) |
1709 | 0 | bUseEditEngine = true; |
1710 | 0 | } |
1711 | | |
1712 | | // Check if this cell is mis-spelled. |
1713 | 0 | if (bDoCell && !bUseEditEngine && aCell.getType() == CELLTYPE_STRING) |
1714 | 0 | { |
1715 | 0 | if (mpSpellCheckCxt && mpSpellCheckCxt->isMisspelled(nCellX, nCellY)) |
1716 | 0 | bUseEditEngine = true; |
1717 | 0 | } |
1718 | |
|
1719 | 0 | if (bDoCell && !bUseEditEngine) |
1720 | 0 | { |
1721 | 0 | if ( nCellY == nY && nCellX >= mnX1 && nCellX <= mnX2 ) |
1722 | 0 | { |
1723 | 0 | ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX); |
1724 | 0 | pPattern = rCellInfo.pPatternAttr; |
1725 | 0 | pCondSet = rCellInfo.pConditionSet; |
1726 | 0 | pTableSet = rCellInfo.pTableFormatSet; |
1727 | |
|
1728 | 0 | if ( !pPattern ) |
1729 | 0 | { |
1730 | | // #i68085# pattern from cell info for hidden columns is null, |
1731 | | // test for null is quicker than using column flags |
1732 | 0 | pPattern = mpDoc->GetPattern( nCellX, nCellY, mnTab ); |
1733 | 0 | pCondSet = mpDoc->GetCondResult( nCellX, nCellY, mnTab ); |
1734 | 0 | pTableSet = mpDoc->GetTableFormatSet( nCellX, nCellY, mnTab ); |
1735 | 0 | } |
1736 | 0 | } |
1737 | 0 | else // get from document |
1738 | 0 | { |
1739 | 0 | pPattern = mpDoc->GetPattern( nCellX, nCellY, mnTab ); |
1740 | 0 | pCondSet = mpDoc->GetCondResult( nCellX, nCellY, mnTab ); |
1741 | 0 | pTableSet = mpDoc->GetTableFormatSet( nCellX, nCellY, mnTab ); |
1742 | 0 | } |
1743 | 0 | if ( mpDoc->GetPreviewFont() || mpDoc->GetPreviewCellStyle() ) |
1744 | 0 | { |
1745 | 0 | aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern)); |
1746 | 0 | ScPatternAttr* pAltPattern = aAltPatterns.back().get(); |
1747 | 0 | if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, mnTab ) ) |
1748 | 0 | { |
1749 | 0 | pAltPattern->SetStyleSheet(pPreviewStyle); |
1750 | 0 | } |
1751 | 0 | else if ( SfxItemSet* pFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, mnTab ) ) |
1752 | 0 | { |
1753 | 0 | if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_FONT ) ) |
1754 | 0 | pAltPattern->ItemSetPut( *pItem ); |
1755 | 0 | if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CJK_FONT ) ) |
1756 | 0 | pAltPattern->ItemSetPut( *pItem ); |
1757 | 0 | if ( const SvxFontItem* pItem = pFontSet->GetItemIfSet( ATTR_CTL_FONT ) ) |
1758 | 0 | pAltPattern->ItemSetPut( *pItem ); |
1759 | 0 | } |
1760 | 0 | pPattern = pAltPattern; |
1761 | 0 | } |
1762 | |
|
1763 | 0 | if (aCell.hasNumeric() && |
1764 | 0 | pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue()) |
1765 | 0 | { |
1766 | | // Disable line break when the cell content is numeric. |
1767 | 0 | aAltPatterns.push_back(std::make_unique<ScPatternAttr>(*pPattern)); |
1768 | 0 | ScPatternAttr* pAltPattern = aAltPatterns.back().get(); |
1769 | 0 | ScLineBreakCell aLineBreak(false); |
1770 | 0 | pAltPattern->ItemSetPut(aLineBreak); |
1771 | 0 | pPattern = pAltPattern; |
1772 | 0 | } |
1773 | |
|
1774 | 0 | SvtScriptType nScript = mpDoc->GetCellScriptType( |
1775 | 0 | ScAddress(nCellX, nCellY, mnTab), |
1776 | 0 | pPattern->GetNumberFormat(mpDoc->GetFormatTable(), pCondSet)); |
1777 | |
|
1778 | 0 | if (nScript == SvtScriptType::NONE) |
1779 | 0 | nScript = ScGlobal::GetDefaultScriptType(); |
1780 | |
|
1781 | 0 | if ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet || |
1782 | 0 | pTableSet != pOldTableSet || nScript != nOldScript || mbSyntaxMode ) |
1783 | 0 | { |
1784 | 0 | if ( StringDiffer(pOldPattern,pPattern) || pCondSet != pOldCondSet || |
1785 | 0 | pTableSet != pOldTableSet || nScript != nOldScript || mbSyntaxMode ) |
1786 | 0 | { |
1787 | 0 | aVars.SetPattern(pPattern, pCondSet, pTableSet, aCell, nScript); |
1788 | 0 | } |
1789 | 0 | else |
1790 | 0 | aVars.SetPatternSimple( pPattern, pCondSet ); |
1791 | 0 | pOldPattern = pPattern; |
1792 | 0 | pOldCondSet = pCondSet; |
1793 | 0 | pOldTableSet = pTableSet; |
1794 | 0 | nOldScript = nScript; |
1795 | 0 | } |
1796 | | |
1797 | | // use edit engine for rotated, stacked or mixed-script text |
1798 | 0 | if ( aVars.GetOrient() == SvxCellOrientation::Stacked || |
1799 | 0 | aVars.IsRotated() || IsAmbiguousScript(nScript) ) |
1800 | 0 | bUseEditEngine = true; |
1801 | 0 | } |
1802 | 0 | if (bDoCell && !bUseEditEngine) |
1803 | 0 | { |
1804 | 0 | bool bFormulaCell = (aCell.getType() == CELLTYPE_FORMULA); |
1805 | 0 | if ( bFormulaCell ) |
1806 | 0 | lcl_CreateInterpretProgress(bProgress, mpDoc, aCell.getFormula()); |
1807 | 0 | if ( aVars.SetText(aCell) ) |
1808 | 0 | pOldPattern = nullptr; |
1809 | 0 | bUseEditEngine = aVars.HasEditCharacters() || (bFormulaCell && aCell.getFormula()->IsMultilineResult()); |
1810 | 0 | } |
1811 | 0 | tools::Long nTotalMargin = 0; |
1812 | 0 | SvxCellHorJustify eOutHorJust = SvxCellHorJustify::Standard; |
1813 | 0 | if (bDoCell && !bUseEditEngine) |
1814 | 0 | { |
1815 | 0 | CellType eCellType = aCell.getType(); |
1816 | 0 | bCellIsValue = ( eCellType == CELLTYPE_VALUE ); |
1817 | 0 | if ( eCellType == CELLTYPE_FORMULA ) |
1818 | 0 | { |
1819 | 0 | ScFormulaCell* pFCell = aCell.getFormula(); |
1820 | 0 | bCellIsValue = pFCell->IsRunning() || pFCell->IsValue(); |
1821 | 0 | } |
1822 | |
|
1823 | 0 | const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, mnTab ); |
1824 | 0 | eOutHorJust = getAlignmentFromContext( aVars.GetHorJust(), bCellIsValue, aVars.GetString(), |
1825 | 0 | *pPattern, pCondSet, mpDoc, mnTab, bNumberFormatIsText ); |
1826 | |
|
1827 | 0 | bool bBreak = ( aVars.GetLineBreak() || aVars.GetHorJust() == SvxCellHorJustify::Block ); |
1828 | | // #i111387# #o11817313# tdf#121040 disable automatic line breaks for all number formats |
1829 | | // Must be synchronized with ScColumn::GetNeededSize() |
1830 | 0 | SvNumberFormatter* pFormatter = mpDoc->GetFormatTable(); |
1831 | 0 | if (bBreak && bCellIsValue && (pFormatter->GetType(aVars.GetResultValueFormat()) == SvNumFormatType::NUMBER)) |
1832 | 0 | bBreak = false; |
1833 | |
|
1834 | 0 | bool bRepeat = aVars.IsRepeat() && !bBreak; |
1835 | 0 | bool bShrink = aVars.IsShrink() && !bBreak && !bRepeat; |
1836 | |
|
1837 | 0 | nTotalMargin = |
1838 | 0 | static_cast<tools::Long>(aVars.GetLeftTotal() * mnPPTX) + |
1839 | 0 | static_cast<tools::Long>(aVars.GetMargin()->GetRightMargin() * mnPPTX); |
1840 | |
|
1841 | 0 | nNeededWidth = aVars.GetTextSize().Width() + nTotalMargin; |
1842 | | |
1843 | | // GetOutputArea gives justified rectangles |
1844 | 0 | GetOutputArea( nX, nArrY, nPosX, nPosY, nCellX, nCellY, nNeededWidth, |
1845 | 0 | *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
1846 | 0 | bCellIsValue || bRepeat || bShrink, bBreak, false, |
1847 | 0 | aAreaParam ); |
1848 | |
|
1849 | 0 | aVars.RepeatToFill( aAreaParam.mnColWidth - nTotalMargin ); |
1850 | 0 | if ( bShrink ) |
1851 | 0 | { |
1852 | 0 | if ( aVars.GetOrient() != SvxCellOrientation::Standard ) |
1853 | 0 | { |
1854 | | // Only horizontal scaling is handled here. |
1855 | | // DrawEdit is used to vertically scale 90 deg rotated text. |
1856 | 0 | bUseEditEngine = true; |
1857 | 0 | } |
1858 | 0 | else if ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) // horizontal |
1859 | 0 | { |
1860 | 0 | tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; |
1861 | 0 | tools::Long nScaleSize = aVars.GetTextSize().Width(); // without margin |
1862 | |
|
1863 | 0 | if ( nAvailable > 0 && nScaleSize > 0 ) // 0 if the text is empty (formulas, number formats) |
1864 | 0 | { |
1865 | 0 | tools::Long nScale = ( nAvailable * 100 ) / nScaleSize; |
1866 | |
|
1867 | 0 | aVars.SetShrinkScale( nScale, nOldScript ); |
1868 | 0 | tools::Long nNewSize = aVars.GetTextSize().Width(); |
1869 | |
|
1870 | 0 | sal_uInt16 nShrinkAgain = 0; |
1871 | 0 | while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) |
1872 | 0 | { |
1873 | | // If the text is still too large, reduce the scale again by 10%, until it fits, |
1874 | | // at most 7 times (it's less than 50% of the calculated scale then). |
1875 | |
|
1876 | 0 | nScale = ( nScale * 9 ) / 10; |
1877 | 0 | aVars.SetShrinkScale( nScale, nOldScript ); |
1878 | 0 | nNewSize = aVars.GetTextSize().Width(); |
1879 | 0 | ++nShrinkAgain; |
1880 | 0 | } |
1881 | | // If even at half the size the font still isn't rendered smaller, |
1882 | | // fall back to normal clipping (showing ### for numbers). |
1883 | 0 | if ( nNewSize <= nAvailable ) |
1884 | 0 | { |
1885 | | // Reset relevant parameters. |
1886 | 0 | aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; |
1887 | 0 | aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; |
1888 | 0 | } |
1889 | |
|
1890 | 0 | pOldPattern = nullptr; |
1891 | 0 | } |
1892 | 0 | } |
1893 | 0 | } |
1894 | |
|
1895 | 0 | if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip ) |
1896 | 0 | { |
1897 | 0 | tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nTotalMargin; |
1898 | 0 | tools::Long nRepeatSize = aVars.GetTextSize().Width(); // without margin |
1899 | | // When formatting for the printer, the text sizes don't always add up. |
1900 | | // Round down (too few repetitions) rather than exceeding the cell size then: |
1901 | 0 | if ( pFmtDevice != mpRefDevice ) |
1902 | 0 | ++nRepeatSize; |
1903 | 0 | if ( nRepeatSize > 0 ) |
1904 | 0 | { |
1905 | 0 | tools::Long nRepeatCount = nAvailable / nRepeatSize; |
1906 | 0 | if ( nRepeatCount > 1 ) |
1907 | 0 | { |
1908 | 0 | OUString aCellStr = aVars.GetString(); |
1909 | 0 | OUStringBuffer aRepeated(aCellStr); |
1910 | 0 | for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) |
1911 | 0 | aRepeated.append(aCellStr); |
1912 | 0 | aVars.SetAutoText( aRepeated.makeStringAndClear() ); |
1913 | 0 | } |
1914 | 0 | } |
1915 | 0 | } |
1916 | | |
1917 | | // use edit engine if automatic line breaks are needed |
1918 | 0 | if ( bBreak ) |
1919 | 0 | { |
1920 | 0 | if ( aVars.GetOrient() == SvxCellOrientation::Standard ) |
1921 | 0 | bUseEditEngine = ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ); |
1922 | 0 | else |
1923 | 0 | { |
1924 | 0 | tools::Long nHeight = aVars.GetTextSize().Height() + |
1925 | 0 | static_cast<tools::Long>(aVars.GetMargin()->GetTopMargin()*mnPPTY) + |
1926 | 0 | static_cast<tools::Long>(aVars.GetMargin()->GetBottomMargin()*mnPPTY); |
1927 | 0 | bUseEditEngine = ( nHeight > aAreaParam.maClipRect.GetHeight() ); |
1928 | 0 | } |
1929 | 0 | } |
1930 | 0 | if (!bUseEditEngine) |
1931 | 0 | { |
1932 | 0 | bUseEditEngine = |
1933 | 0 | aVars.GetHorJust() == SvxCellHorJustify::Block && |
1934 | 0 | aVars.GetHorJustMethod() == SvxCellJustifyMethod::Distribute; |
1935 | 0 | } |
1936 | 0 | } |
1937 | 0 | if (bUseEditEngine) |
1938 | 0 | { |
1939 | | // mark the cell in ScCellInfo to be drawn in DrawEdit: |
1940 | | // Cells to the left are marked directly, cells to the |
1941 | | // right are handled by the flag for mnX2 |
1942 | 0 | SCCOL nMarkX = ( nCellX <= mnX2 ) ? nCellX : mnX2; |
1943 | 0 | pThisRowInfo->basicCellInfo(nMarkX).bEditEngine = true; |
1944 | 0 | bDoCell = false; // don't draw here |
1945 | | |
1946 | | // Mark the tagged "TD" structure element to be drawn in DrawEdit |
1947 | 0 | if (bTaggedPDF) |
1948 | 0 | { |
1949 | 0 | if (bReopenRowTag) |
1950 | 0 | ReopenPDFStructureElement(vcl::pdf::StructElement::TableRow, nY); |
1951 | 0 | else |
1952 | 0 | { |
1953 | 0 | sal_Int32 nId = pPDF->EnsureStructureElement(nullptr); |
1954 | 0 | pPDF->InitStructureElement(nId, vcl::pdf::StructElement::TableRow, |
1955 | 0 | u"TR"_ustr); |
1956 | 0 | pPDF->BeginStructureElement(nId); |
1957 | 0 | pPDF->GetScPDFState()->m_TableRowMap.emplace(nY, nId); |
1958 | 0 | bReopenRowTag = true; |
1959 | 0 | } |
1960 | |
|
1961 | 0 | pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::TableData, |
1962 | 0 | u"TD"_ustr); |
1963 | |
|
1964 | 0 | sal_Int32 nId = pPDF->GetCurrentStructureElement(); |
1965 | 0 | pPDF->GetScPDFState()->m_TableDataMap[{ nY, nX }] = nId; |
1966 | |
|
1967 | 0 | pPDF->EndStructureElement(); // TableData |
1968 | 0 | pPDF->EndStructureElement(); // TableRow |
1969 | 0 | } |
1970 | 0 | } |
1971 | 0 | if ( bDoCell ) |
1972 | 0 | { |
1973 | 0 | if ( bCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) |
1974 | 0 | { |
1975 | 0 | bool bHasHashText = false; |
1976 | 0 | if (mbShowFormulas) |
1977 | 0 | { |
1978 | 0 | aVars.SetHashText(); |
1979 | 0 | bHasHashText = true; |
1980 | 0 | } |
1981 | 0 | else |
1982 | | // Adjust the decimals to fit the available column width. |
1983 | 0 | bHasHashText = aVars.SetTextToWidthOrHash( aCell, aAreaParam.mnColWidth - nTotalMargin ); |
1984 | |
|
1985 | 0 | if ( bHasHashText ) |
1986 | 0 | { |
1987 | 0 | tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX; |
1988 | |
|
1989 | 0 | if ( eOutHorJust == SvxCellHorJustify::Left ) |
1990 | 0 | { |
1991 | 0 | if ( nCellY == nY && nCellX >= mnX1 && nCellX <= mnX2 ) |
1992 | 0 | mpRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; |
1993 | 0 | mbAnyClipped = true; |
1994 | 0 | aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); |
1995 | 0 | } |
1996 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Right ) |
1997 | 0 | { |
1998 | 0 | if ( nCellY == nY && nCellX >= mnX1 && nCellX <= mnX2 ) |
1999 | 0 | mpRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; |
2000 | 0 | mbAnyClipped = true; |
2001 | 0 | aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); |
2002 | 0 | } |
2003 | 0 | else |
2004 | 0 | { |
2005 | 0 | if ( nCellY == nY && nCellX >= mnX1 && nCellX <= mnX2 ) |
2006 | 0 | { |
2007 | 0 | mpRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Right; |
2008 | 0 | mpRowInfo[nArrY].cellInfo(nCellX).nClipMark |= ScClipMark::Left; |
2009 | 0 | } |
2010 | 0 | mbAnyClipped = true; |
2011 | 0 | aAreaParam.maClipRect.AdjustRight( -(nMarkPixel * nLayoutSign) ); |
2012 | 0 | aAreaParam.maClipRect.AdjustLeft(nMarkPixel * nLayoutSign); |
2013 | 0 | } |
2014 | 0 | } |
2015 | |
|
2016 | 0 | nNeededWidth = aVars.GetTextSize().Width() + |
2017 | 0 | static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) + |
2018 | 0 | static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ); |
2019 | 0 | if ( nNeededWidth <= aAreaParam.maClipRect.GetWidth() ) |
2020 | 0 | { |
2021 | | // Cell value is no longer clipped. Reset relevant parameters. |
2022 | 0 | aAreaParam.mbLeftClip = aAreaParam.mbRightClip = false; |
2023 | 0 | aAreaParam.mnLeftClipLength = aAreaParam.mnRightClipLength = 0; |
2024 | 0 | } |
2025 | 0 | } |
2026 | |
|
2027 | 0 | tools::Long nJustPosX = aAreaParam.maAlignRect.Left(); // "justified" - effect of alignment will be added |
2028 | 0 | tools::Long nJustPosY = aAreaParam.maAlignRect.Top(); |
2029 | 0 | tools::Long nAvailWidth = aAreaParam.maAlignRect.GetWidth(); |
2030 | 0 | tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight(); |
2031 | |
|
2032 | 0 | bool bOutside = ( aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW ); |
2033 | | // Take adjusted values of aAreaParam.mbLeftClip and aAreaParam.mbRightClip |
2034 | 0 | bool bVClip = AdjustAreaParamClipRect(aAreaParam); |
2035 | 0 | bool bHClip = aAreaParam.mbLeftClip || aAreaParam.mbRightClip; |
2036 | | |
2037 | | // check horizontal space |
2038 | |
|
2039 | 0 | if ( !bOutside ) |
2040 | 0 | { |
2041 | 0 | bool bRightAdjusted = false; // to correct text width calculation later |
2042 | 0 | switch (eOutHorJust) |
2043 | 0 | { |
2044 | 0 | case SvxCellHorJustify::Left: |
2045 | 0 | nJustPosX += static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ); |
2046 | 0 | break; |
2047 | 0 | case SvxCellHorJustify::Right: |
2048 | 0 | nJustPosX += nAvailWidth - aVars.GetTextSize().Width() - |
2049 | 0 | static_cast<tools::Long>( aVars.GetRightTotal() * mnPPTX ); |
2050 | 0 | bRightAdjusted = true; |
2051 | 0 | break; |
2052 | 0 | case SvxCellHorJustify::Center: |
2053 | 0 | nJustPosX += ( nAvailWidth - aVars.GetTextSize().Width() + |
2054 | 0 | static_cast<tools::Long>( aVars.GetLeftTotal() * mnPPTX ) - |
2055 | 0 | static_cast<tools::Long>( aVars.GetMargin()->GetRightMargin() * mnPPTX ) ) / 2; |
2056 | 0 | break; |
2057 | 0 | default: |
2058 | 0 | { |
2059 | | // added to avoid warnings |
2060 | 0 | } |
2061 | 0 | } |
2062 | | |
2063 | 0 | tools::Long nTestClipHeight = aVars.GetTextSize().Height(); |
2064 | 0 | switch (aVars.GetVerJust()) |
2065 | 0 | { |
2066 | 0 | case SvxCellVerJustify::Top: |
2067 | 0 | case SvxCellVerJustify::Block: |
2068 | 0 | { |
2069 | 0 | tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); |
2070 | 0 | nJustPosY += nTop; |
2071 | 0 | nTestClipHeight += nTop; |
2072 | 0 | } |
2073 | 0 | break; |
2074 | 0 | case SvxCellVerJustify::Bottom: |
2075 | 0 | { |
2076 | 0 | tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); |
2077 | 0 | nJustPosY += nOutHeight - aVars.GetTextSize().Height() - nBot; |
2078 | 0 | nTestClipHeight += nBot; |
2079 | 0 | } |
2080 | 0 | break; |
2081 | 0 | case SvxCellVerJustify::Center: |
2082 | 0 | { |
2083 | 0 | tools::Long nTop = static_cast<tools::Long>( aVars.GetMargin()->GetTopMargin() * mnPPTY ); |
2084 | 0 | tools::Long nBot = static_cast<tools::Long>( aVars.GetMargin()->GetBottomMargin() * mnPPTY ); |
2085 | 0 | nJustPosY += ( nOutHeight + nTop - |
2086 | 0 | aVars.GetTextSize().Height() - nBot ) / 2; |
2087 | 0 | nTestClipHeight += std::abs( nTop - nBot ); |
2088 | 0 | } |
2089 | 0 | break; |
2090 | 0 | default: |
2091 | 0 | { |
2092 | | // added to avoid warnings |
2093 | 0 | } |
2094 | 0 | } |
2095 | | |
2096 | 0 | if ( nTestClipHeight > nOutHeight ) |
2097 | 0 | { |
2098 | | // no vertical clipping when printing cells with optimal height, |
2099 | | // except when font size is from conditional formatting. |
2100 | 0 | if ( meType != OUTTYPE_PRINTER || |
2101 | 0 | ( mpDoc->GetRowFlags( nCellY, mnTab ) & CRFlags::ManualSize ) || |
2102 | 0 | ( aVars.HasCondHeight() ) ) |
2103 | 0 | bVClip = true; |
2104 | 0 | } |
2105 | |
|
2106 | 0 | if ( bHClip || bVClip ) |
2107 | 0 | { |
2108 | | // only clip the affected dimension so that not all right-aligned |
2109 | | // columns are cut off when performing a non-proportional resize |
2110 | 0 | if (!bHClip) |
2111 | 0 | { |
2112 | 0 | aAreaParam.maClipRect.SetLeft( mnScrX ); |
2113 | 0 | aAreaParam.maClipRect.SetRight( mnScrX+mnScrW ); |
2114 | 0 | } |
2115 | 0 | if (!bVClip) |
2116 | 0 | { |
2117 | 0 | aAreaParam.maClipRect.SetTop( mnScrY ); |
2118 | 0 | aAreaParam.maClipRect.SetBottom( mnScrY+mnScrH ); |
2119 | 0 | } |
2120 | | |
2121 | | // aClipRect is not used after SetClipRegion/IntersectClipRegion, |
2122 | | // so it can be modified here |
2123 | 0 | if (bPixelToLogic) |
2124 | 0 | aAreaParam.maClipRect = mpRefDevice->PixelToLogic( aAreaParam.maClipRect ); |
2125 | |
|
2126 | 0 | if (mbMetaFile) |
2127 | 0 | { |
2128 | 0 | mpDev->Push(); |
2129 | 0 | mpDev->IntersectClipRegion( aAreaParam.maClipRect ); |
2130 | 0 | } |
2131 | 0 | else |
2132 | 0 | mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) ); |
2133 | 0 | } |
2134 | |
|
2135 | 0 | Point aURLStart( nJustPosX, nJustPosY ); // copy before modifying for orientation |
2136 | |
|
2137 | 0 | switch (aVars.GetOrient()) |
2138 | 0 | { |
2139 | 0 | case SvxCellOrientation::Standard: |
2140 | 0 | nJustPosY += aVars.GetAscent(); |
2141 | 0 | break; |
2142 | 0 | case SvxCellOrientation::TopBottom: |
2143 | 0 | nJustPosX += aVars.GetTextSize().Width() - aVars.GetAscent(); |
2144 | 0 | break; |
2145 | 0 | case SvxCellOrientation::BottomUp: |
2146 | 0 | nJustPosY += aVars.GetTextSize().Height(); |
2147 | 0 | nJustPosX += aVars.GetAscent(); |
2148 | 0 | break; |
2149 | 0 | default: |
2150 | 0 | { |
2151 | | // added to avoid warnings |
2152 | 0 | } |
2153 | 0 | } |
2154 | | |
2155 | | // When clipping, the visible part is now completely defined by the alignment, |
2156 | | // there's no more special handling to show the right part of RTL text. |
2157 | | |
2158 | 0 | Point aDrawTextPos( nJustPosX, nJustPosY ); |
2159 | 0 | if ( bPixelToLogic ) |
2160 | 0 | { |
2161 | | // undo text width adjustment in pixels |
2162 | 0 | if (bRightAdjusted) |
2163 | 0 | aDrawTextPos.AdjustX(aVars.GetTextSize().Width() ); |
2164 | |
|
2165 | 0 | aDrawTextPos = mpRefDevice->PixelToLogic( aDrawTextPos ); |
2166 | | |
2167 | | // redo text width adjustment in logic units |
2168 | 0 | if (bRightAdjusted) |
2169 | 0 | aDrawTextPos.AdjustX( -(aVars.GetOriginalWidth()) ); |
2170 | 0 | } |
2171 | | |
2172 | | // in Metafiles always use DrawTextArray to ensure that positions are |
2173 | | // recorded (for non-proportional resize): |
2174 | |
|
2175 | 0 | const OUString& aString = aVars.GetString(); |
2176 | 0 | if (!aString.isEmpty()) |
2177 | 0 | { |
2178 | 0 | if (bTaggedPDF) |
2179 | 0 | { |
2180 | 0 | if (bReopenRowTag) |
2181 | 0 | ReopenPDFStructureElement(vcl::pdf::StructElement::TableRow, |
2182 | 0 | nY); |
2183 | 0 | else |
2184 | 0 | { |
2185 | 0 | sal_Int32 nId = pPDF->EnsureStructureElement(nullptr); |
2186 | 0 | pPDF->InitStructureElement( |
2187 | 0 | nId, vcl::pdf::StructElement::TableRow, u"TR"_ustr); |
2188 | 0 | pPDF->BeginStructureElement(nId); |
2189 | 0 | pPDF->GetScPDFState()->m_TableRowMap.emplace(nY, nId); |
2190 | 0 | bReopenRowTag = true; |
2191 | 0 | } |
2192 | |
|
2193 | 0 | pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::TableData, |
2194 | 0 | u"TD"_ustr); |
2195 | 0 | pPDF->WrapBeginStructureElement(vcl::pdf::StructElement::Paragraph, |
2196 | 0 | u"P"_ustr); |
2197 | 0 | } |
2198 | | |
2199 | | // If the string is clipped, make it shorter for |
2200 | | // better performance since drawing by HarfBuzz is |
2201 | | // quite expensive especially for long string. |
2202 | |
|
2203 | 0 | OUString aShort = aString; |
2204 | | |
2205 | | // But never fiddle with numeric values. |
2206 | | // (Which was the cause of tdf#86024). |
2207 | | // The General automatic format output takes |
2208 | | // care of this, or fixed width numbers either fit |
2209 | | // or display as ###. |
2210 | 0 | if (!bCellIsValue) |
2211 | 0 | { |
2212 | 0 | double fVisibleRatio = 1.0; |
2213 | 0 | double fTextWidth = aVars.GetTextSize().Width(); |
2214 | 0 | sal_Int32 nTextLen = aString.getLength(); |
2215 | 0 | if (eOutHorJust == SvxCellHorJustify::Left && aAreaParam.mnRightClipLength > 0) |
2216 | 0 | { |
2217 | 0 | fVisibleRatio = (fTextWidth - aAreaParam.mnRightClipLength) / fTextWidth; |
2218 | 0 | if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) |
2219 | 0 | { |
2220 | | // Only show the left-end segment. |
2221 | 0 | sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; |
2222 | 0 | aShort = aShort.copy(0, nShortLen); |
2223 | 0 | } |
2224 | 0 | } |
2225 | 0 | else if (eOutHorJust == SvxCellHorJustify::Right && aAreaParam.mnLeftClipLength > 0) |
2226 | 0 | { |
2227 | 0 | fVisibleRatio = (fTextWidth - aAreaParam.mnLeftClipLength) / fTextWidth; |
2228 | 0 | if (0.0 < fVisibleRatio && fVisibleRatio < 1.0) |
2229 | 0 | { |
2230 | | // Only show the right-end segment. |
2231 | 0 | sal_Int32 nShortLen = fVisibleRatio*nTextLen + 1; |
2232 | 0 | aShort = aShort.copy(nTextLen-nShortLen); |
2233 | | |
2234 | | // Adjust the text position after shortening of the string. |
2235 | 0 | double fShortWidth = aVars.GetFmtTextWidth(aShort); |
2236 | 0 | double fOffset = fTextWidth - fShortWidth; |
2237 | 0 | aDrawTextPos.Move(fOffset, 0); |
2238 | 0 | } |
2239 | 0 | } |
2240 | 0 | } |
2241 | |
|
2242 | 0 | if (mbMetaFile || pFmtDevice != mpDev || mfZoomX != mfZoomY) |
2243 | 0 | { |
2244 | 0 | size_t nLen = aShort.getLength(); |
2245 | 0 | if (aDX.size() < nLen) |
2246 | 0 | aDX.resize(nLen, 0); |
2247 | |
|
2248 | 0 | pFmtDevice->GetTextArray(aShort, &aDX); |
2249 | |
|
2250 | 0 | if ( !mpRefDevice->GetConnectMetaFile() || |
2251 | 0 | mpRefDevice->GetOutDevType() == OUTDEV_PRINTER ) |
2252 | 0 | { |
2253 | 0 | double fMul = GetStretch(); |
2254 | 0 | for (size_t i = 0; i < nLen; ++i) |
2255 | 0 | aDX[i] /= fMul; |
2256 | 0 | } |
2257 | |
|
2258 | 0 | mpDev->DrawTextArray(aDrawTextPos, aShort, aDX, {}, 0, nLen); |
2259 | 0 | } |
2260 | 0 | else |
2261 | 0 | { |
2262 | 0 | mpDev->DrawText(aDrawTextPos, aShort, 0, -1, nullptr, nullptr, |
2263 | 0 | aVars.GetLayoutGlyphs(aShort)); |
2264 | 0 | } |
2265 | 0 | if (bTaggedPDF) |
2266 | 0 | { |
2267 | 0 | pPDF->EndStructureElement(); // Paragraph |
2268 | 0 | pPDF->EndStructureElement(); // TableData |
2269 | 0 | pPDF->EndStructureElement(); // TableRow |
2270 | 0 | } |
2271 | 0 | } |
2272 | |
|
2273 | 0 | if ( bHClip || bVClip ) |
2274 | 0 | { |
2275 | 0 | if (mbMetaFile) |
2276 | 0 | mpDev->Pop(); |
2277 | 0 | else |
2278 | 0 | mpDev->SetClipRegion(); |
2279 | 0 | } |
2280 | | |
2281 | | // PDF: whole-cell hyperlink from formula? |
2282 | 0 | bool bHasURL = pPDF && aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->IsHyperLinkCell(); |
2283 | 0 | if (bHasURL) |
2284 | 0 | { |
2285 | 0 | tools::Rectangle aURLRect( aURLStart, aVars.GetTextSize() ); |
2286 | 0 | lcl_DoHyperlinkResult(mpDev, aURLRect, aCell); |
2287 | 0 | } |
2288 | 0 | } |
2289 | 0 | } |
2290 | 0 | } |
2291 | | |
2292 | | void ScOutputData::SetRefDevice( OutputDevice* pRDev ) |
2293 | 0 | { |
2294 | 0 | mpRefDevice = pFmtDevice = pRDev; |
2295 | | // reset EditEngine because it depends on pFmtDevice and mpRefDevice |
2296 | 0 | mxOutputEditEngine.reset(); |
2297 | 0 | } |
2298 | | |
2299 | | void ScOutputData::SetFmtDevice( OutputDevice* pRDev ) |
2300 | 0 | { |
2301 | 0 | pFmtDevice = pRDev; |
2302 | | // reset EditEngine because it depends on pFmtDevice |
2303 | 0 | mxOutputEditEngine.reset(); |
2304 | 0 | } |
2305 | | |
2306 | | void ScOutputData::SetUseStyleColor( bool bSet ) |
2307 | 0 | { |
2308 | 0 | mbUseStyleColor = bSet; |
2309 | | // reset EditEngine because it depends on mbUseStyleColor |
2310 | 0 | mxOutputEditEngine.reset(); |
2311 | 0 | } |
2312 | | |
2313 | | void ScOutputData::InitOutputEditEngine() |
2314 | 0 | { |
2315 | 0 | if (!mxOutputEditEngine) |
2316 | 0 | { |
2317 | 0 | mxOutputEditEngine = std::make_unique<ScFieldEditEngine>(mpDoc, mpDoc->GetEditEnginePool()); |
2318 | 0 | mxOutputEditEngine->SetUpdateLayout( false ); |
2319 | 0 | mxOutputEditEngine->EnableUndo( false ); // don't need undo for painting purposes |
2320 | | // a RefDevice always has to be set, otherwise EditEngine would create a VirtualDevice |
2321 | 0 | mxOutputEditEngine->SetRefDevice( pFmtDevice ); |
2322 | 0 | EEControlBits nCtrl = mxOutputEditEngine->GetControlWord(); |
2323 | 0 | if ( mbShowSpellErrors ) |
2324 | 0 | nCtrl |= EEControlBits::ONLINESPELLING; |
2325 | 0 | if ( meType == OUTTYPE_PRINTER ) |
2326 | 0 | nCtrl &= ~EEControlBits::MARKFIELDS; |
2327 | 0 | else |
2328 | 0 | nCtrl &= ~EEControlBits::MARKURLFIELDS; // URLs not shaded for output |
2329 | 0 | mxOutputEditEngine->SetControlWord( nCtrl ); |
2330 | 0 | mxOutputEditEngine->EnableAutoColor( mbUseStyleColor ); |
2331 | 0 | } |
2332 | 0 | else |
2333 | 0 | { |
2334 | | // just in case someone turned it on during the last paint cycle |
2335 | 0 | mxOutputEditEngine->SetUpdateLayout( false ); |
2336 | 0 | } |
2337 | | // we don't track changes to these settings, so we have to apply them every time |
2338 | 0 | mpDoc->ApplyAsianEditSettings( *mxOutputEditEngine ); |
2339 | 0 | mxOutputEditEngine->SetDefaultHorizontalTextDirection( mpDoc->GetEditTextDirection( mnTab ) ); |
2340 | 0 | } |
2341 | | |
2342 | | static void lcl_ClearEdit( EditEngine& rEngine ) // text and attributes |
2343 | 0 | { |
2344 | 0 | rEngine.SetUpdateLayout( false ); |
2345 | |
|
2346 | 0 | rEngine.SetText(OUString()); |
2347 | | // do not keep any para-attributes |
2348 | 0 | const SfxItemSet& rPara = rEngine.GetParaAttribs(0); |
2349 | 0 | if (rPara.Count()) |
2350 | 0 | rEngine.SetParaAttribs( 0, |
2351 | 0 | SfxItemSet( *rPara.GetPool(), rPara.GetRanges() ) ); |
2352 | 0 | rEngine.EnableSkipOutsideFormat(false); |
2353 | 0 | } |
2354 | | |
2355 | | static bool lcl_SafeIsValue( const ScRefCellValue& rCell ) |
2356 | 0 | { |
2357 | 0 | switch (rCell.getType()) |
2358 | 0 | { |
2359 | 0 | case CELLTYPE_VALUE: |
2360 | 0 | return true; |
2361 | 0 | case CELLTYPE_FORMULA: |
2362 | 0 | { |
2363 | 0 | ScFormulaCell* pFCell = rCell.getFormula(); |
2364 | 0 | if (pFCell->IsRunning() || pFCell->IsValue()) |
2365 | 0 | return true; |
2366 | 0 | } |
2367 | 0 | break; |
2368 | 0 | default: |
2369 | 0 | { |
2370 | | // added to avoid warnings |
2371 | 0 | } |
2372 | 0 | } |
2373 | 0 | return false; |
2374 | 0 | } |
2375 | | |
2376 | | static void lcl_ScaleFonts( EditEngine& rEngine, tools::Long nPercent ) |
2377 | 0 | { |
2378 | 0 | bool bUpdateMode = rEngine.SetUpdateLayout( false ); |
2379 | |
|
2380 | 0 | sal_Int32 nParCount = rEngine.GetParagraphCount(); |
2381 | 0 | for (sal_Int32 nPar=0; nPar<nParCount; nPar++) |
2382 | 0 | { |
2383 | 0 | std::vector<sal_Int32> aPortions; |
2384 | 0 | rEngine.GetPortions( nPar, aPortions ); |
2385 | |
|
2386 | 0 | sal_Int32 nStart = 0; |
2387 | 0 | for ( const sal_Int32 nEnd : aPortions ) |
2388 | 0 | { |
2389 | 0 | ESelection aSel( nPar, nStart, nPar, nEnd ); |
2390 | 0 | SfxItemSet aAttribs = rEngine.GetAttribs( aSel ); |
2391 | |
|
2392 | 0 | tools::Long nWestern = aAttribs.Get(EE_CHAR_FONTHEIGHT).GetHeight(); |
2393 | 0 | tools::Long nCJK = aAttribs.Get(EE_CHAR_FONTHEIGHT_CJK).GetHeight(); |
2394 | 0 | tools::Long nCTL = aAttribs.Get(EE_CHAR_FONTHEIGHT_CTL).GetHeight(); |
2395 | |
|
2396 | 0 | nWestern = ( nWestern * nPercent ) / 100; |
2397 | 0 | nCJK = ( nCJK * nPercent ) / 100; |
2398 | 0 | nCTL = ( nCTL * nPercent ) / 100; |
2399 | |
|
2400 | 0 | aAttribs.Put( SvxFontHeightItem( nWestern, 100, EE_CHAR_FONTHEIGHT ) ); |
2401 | 0 | aAttribs.Put( SvxFontHeightItem( nCJK, 100, EE_CHAR_FONTHEIGHT_CJK ) ); |
2402 | 0 | aAttribs.Put( SvxFontHeightItem( nCTL, 100, EE_CHAR_FONTHEIGHT_CTL ) ); |
2403 | |
|
2404 | 0 | rEngine.QuickSetAttribs( aAttribs, aSel ); //! remove paragraph attributes from aAttribs? |
2405 | |
|
2406 | 0 | nStart = nEnd; |
2407 | 0 | } |
2408 | 0 | } |
2409 | |
|
2410 | 0 | if ( bUpdateMode ) |
2411 | 0 | rEngine.SetUpdateLayout( true ); |
2412 | 0 | } |
2413 | | |
2414 | | static tools::Long lcl_GetEditSize( EditEngine& rEngine, bool bWidth, bool bSwap, Degree100 nAttrRotate ) |
2415 | 0 | { |
2416 | 0 | if ( bSwap ) |
2417 | 0 | bWidth = !bWidth; |
2418 | |
|
2419 | 0 | if ( nAttrRotate ) |
2420 | 0 | { |
2421 | 0 | tools::Long nRealWidth = static_cast<tools::Long>(rEngine.CalcTextWidth()); |
2422 | 0 | tools::Long nRealHeight = rEngine.GetTextHeight(); |
2423 | | |
2424 | | // assuming standard mode, otherwise width isn't used |
2425 | |
|
2426 | 0 | double nRealOrient = toRadians(nAttrRotate); // 1/100th degrees |
2427 | 0 | double nAbsCos = fabs( cos( nRealOrient ) ); |
2428 | 0 | double nAbsSin = fabs( sin( nRealOrient ) ); |
2429 | 0 | if ( bWidth ) |
2430 | 0 | return static_cast<tools::Long>( nRealWidth * nAbsCos + nRealHeight * nAbsSin ); |
2431 | 0 | else |
2432 | 0 | return static_cast<tools::Long>( nRealHeight * nAbsCos + nRealWidth * nAbsSin ); |
2433 | 0 | } |
2434 | 0 | else if ( bWidth ) |
2435 | 0 | return static_cast<tools::Long>(rEngine.CalcTextWidth()); |
2436 | 0 | else |
2437 | 0 | return rEngine.GetTextHeight(); |
2438 | 0 | } |
2439 | | |
2440 | | void ScOutputData::ShrinkEditEngine( EditEngine& rEngine, const tools::Rectangle& rAlignRect, |
2441 | | tools::Long nLeftM, tools::Long nTopM, tools::Long nRightM, tools::Long nBottomM, |
2442 | | bool bWidth, SvxCellOrientation nOrient, Degree100 nAttrRotate, bool bPixelToLogic, |
2443 | | tools::Long& rEngineWidth, tools::Long& rEngineHeight, tools::Long& rNeededPixel, bool& rLeftClip, bool& rRightClip ) |
2444 | 0 | { |
2445 | 0 | if ( !bWidth ) |
2446 | 0 | { |
2447 | | // vertical |
2448 | |
|
2449 | 0 | tools::Long nScaleSize = bPixelToLogic ? |
2450 | 0 | mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; |
2451 | | |
2452 | | // Don't scale if it fits already. |
2453 | | // Allowing to extend into the margin, to avoid scaling at optimal height. |
2454 | 0 | if ( nScaleSize <= rAlignRect.GetHeight() ) |
2455 | 0 | return; |
2456 | | |
2457 | 0 | bool bSwap = ( nOrient == SvxCellOrientation::TopBottom || nOrient == SvxCellOrientation::BottomUp ); |
2458 | 0 | tools::Long nAvailable = rAlignRect.GetHeight() - nTopM - nBottomM; |
2459 | 0 | tools::Long nScale = ( nAvailable * 100 ) / nScaleSize; |
2460 | |
|
2461 | 0 | lcl_ScaleFonts( rEngine, nScale ); |
2462 | 0 | rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate ); |
2463 | 0 | tools::Long nNewSize = bPixelToLogic ? |
2464 | 0 | mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; |
2465 | |
|
2466 | 0 | sal_uInt16 nShrinkAgain = 0; |
2467 | 0 | while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) |
2468 | 0 | { |
2469 | | // further reduce, like in DrawStrings |
2470 | 0 | lcl_ScaleFonts( rEngine, 90 ); // reduce by 10% |
2471 | 0 | rEngineHeight = lcl_GetEditSize( rEngine, false, bSwap, nAttrRotate ); |
2472 | 0 | nNewSize = bPixelToLogic ? |
2473 | 0 | mpRefDevice->LogicToPixel(Size(0,rEngineHeight)).Height() : rEngineHeight; |
2474 | 0 | ++nShrinkAgain; |
2475 | 0 | } |
2476 | | |
2477 | | // sizes for further processing (alignment etc): |
2478 | 0 | rEngineWidth = lcl_GetEditSize( rEngine, true, bSwap, nAttrRotate ); |
2479 | 0 | tools::Long nPixelWidth = bPixelToLogic ? |
2480 | 0 | mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; |
2481 | 0 | rNeededPixel = nPixelWidth + nLeftM + nRightM; |
2482 | 0 | } |
2483 | 0 | else if ( rLeftClip || rRightClip ) |
2484 | 0 | { |
2485 | | // horizontal |
2486 | |
|
2487 | 0 | tools::Long nAvailable = rAlignRect.GetWidth() - nLeftM - nRightM; |
2488 | 0 | tools::Long nScaleSize = rNeededPixel - nLeftM - nRightM; // without margin |
2489 | |
|
2490 | 0 | if ( nScaleSize <= nAvailable ) |
2491 | 0 | return; |
2492 | | |
2493 | 0 | tools::Long nScale = ( nAvailable * 100 ) / nScaleSize; |
2494 | |
|
2495 | 0 | lcl_ScaleFonts( rEngine, nScale ); |
2496 | 0 | rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate ); |
2497 | 0 | tools::Long nNewSize = bPixelToLogic ? |
2498 | 0 | mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; |
2499 | |
|
2500 | 0 | sal_uInt16 nShrinkAgain = 0; |
2501 | 0 | while ( nNewSize > nAvailable && nShrinkAgain < SC_SHRINKAGAIN_MAX ) |
2502 | 0 | { |
2503 | | // further reduce, like in DrawStrings |
2504 | 0 | lcl_ScaleFonts( rEngine, 90 ); // reduce by 10% |
2505 | 0 | rEngineWidth = lcl_GetEditSize( rEngine, true, false, nAttrRotate ); |
2506 | 0 | nNewSize = bPixelToLogic ? |
2507 | 0 | mpRefDevice->LogicToPixel(Size(rEngineWidth,0)).Width() : rEngineWidth; |
2508 | 0 | ++nShrinkAgain; |
2509 | 0 | } |
2510 | 0 | if ( nNewSize <= nAvailable ) |
2511 | 0 | rLeftClip = rRightClip = false; |
2512 | | |
2513 | | // sizes for further processing (alignment etc): |
2514 | 0 | rNeededPixel = nNewSize + nLeftM + nRightM; |
2515 | 0 | rEngineHeight = lcl_GetEditSize( rEngine, false, false, nAttrRotate ); |
2516 | 0 | } |
2517 | 0 | } |
2518 | | |
2519 | | ScOutputData::DrawEditParam::DrawEditParam(const ScPatternAttr* pPattern, const SfxItemSet* pCondSet, const SfxItemSet* pTableSet, bool bCellIsValue) : |
2520 | 0 | meHorJustAttr( lcl_GetValue<SvxHorJustifyItem, SvxCellHorJustify>(*pPattern, ATTR_HOR_JUSTIFY, pCondSet) ), |
2521 | 0 | meHorJustContext( meHorJustAttr ), |
2522 | 0 | meHorJustResult( meHorJustAttr ), |
2523 | 0 | meVerJust( lcl_GetValue<SvxVerJustifyItem, SvxCellVerJustify>(*pPattern, ATTR_VER_JUSTIFY, pCondSet) ), |
2524 | 0 | meHorJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_HOR_JUSTIFY_METHOD, pCondSet) ), |
2525 | 0 | meVerJustMethod( lcl_GetValue<SvxJustifyMethodItem, SvxCellJustifyMethod>(*pPattern, ATTR_VER_JUSTIFY_METHOD, pCondSet) ), |
2526 | 0 | meOrient( pPattern->GetCellOrientation(pCondSet) ), |
2527 | 0 | mnArrY(0), |
2528 | 0 | mnX(0), mnCellX(0), mnCellY(0), |
2529 | 0 | mnPosX(0), mnPosY(0), mnInitPosX(0), |
2530 | 0 | mbBreak( (meHorJustAttr == SvxCellHorJustify::Block) || lcl_GetBoolValue(*pPattern, ATTR_LINEBREAK, pCondSet) ), |
2531 | 0 | mbCellIsValue(bCellIsValue), |
2532 | 0 | mbAsianVertical(false), |
2533 | 0 | mbPixelToLogic(false), |
2534 | 0 | mbHyphenatorSet(false), |
2535 | 0 | mpEngine(nullptr), |
2536 | 0 | mpPattern(pPattern), |
2537 | 0 | mpCondSet(pCondSet), |
2538 | 0 | mpTableSet(pTableSet), |
2539 | 0 | mpPreviewFontSet(nullptr), |
2540 | 0 | mpOldPattern(nullptr), |
2541 | 0 | mpOldCondSet(nullptr), |
2542 | 0 | mpOldTableSet(nullptr), |
2543 | 0 | mpOldPreviewFontSet(nullptr), |
2544 | 0 | mpThisRowInfo(nullptr) |
2545 | 0 | {} |
2546 | | |
2547 | | bool ScOutputData::DrawEditParam::readCellContent( |
2548 | | const ScDocument* pDoc, bool bShowNullValues, bool bShowFormulas, bool bSyntaxMode, bool bUseStyleColor, bool bForceAutoColor, bool& rWrapFields) |
2549 | 0 | { |
2550 | 0 | if (maCell.getType() == CELLTYPE_EDIT) |
2551 | 0 | { |
2552 | 0 | const EditTextObject* pData = maCell.getEditText(); |
2553 | 0 | if (pData) |
2554 | 0 | { |
2555 | 0 | mpEngine->SetTextCurrentDefaults(*pData); |
2556 | |
|
2557 | 0 | if ( mbBreak && !mbAsianVertical && pData->HasField() ) |
2558 | 0 | { |
2559 | | // Fields aren't wrapped, so clipping is enabled to prevent |
2560 | | // a field from being drawn beyond the cell size |
2561 | |
|
2562 | 0 | rWrapFields = true; |
2563 | 0 | } |
2564 | 0 | } |
2565 | 0 | else |
2566 | 0 | { |
2567 | 0 | OSL_FAIL("pData == 0"); |
2568 | 0 | return false; |
2569 | 0 | } |
2570 | 0 | } |
2571 | 0 | else |
2572 | 0 | { |
2573 | 0 | sal_uInt32 nFormat = mpPattern->GetNumberFormat( |
2574 | 0 | pDoc->GetFormatTable(), mpCondSet ); |
2575 | 0 | const Color* pColor; |
2576 | 0 | OUString aString = ScCellFormat::GetString( maCell, |
2577 | 0 | nFormat, &pColor, |
2578 | 0 | nullptr, |
2579 | 0 | *pDoc, |
2580 | 0 | bShowNullValues, |
2581 | 0 | bShowFormulas); |
2582 | |
|
2583 | 0 | mpEngine->SetTextCurrentDefaults(aString); |
2584 | 0 | if ( pColor && !bSyntaxMode && !( bUseStyleColor && bForceAutoColor ) ) |
2585 | 0 | lcl_SetEditColor( *mpEngine, *pColor ); |
2586 | 0 | } |
2587 | | |
2588 | 0 | if (maMisspellRanges.mpRanges) |
2589 | 0 | mpEngine->SetAllMisspellRanges(*maMisspellRanges.mpRanges); |
2590 | |
|
2591 | 0 | return true; |
2592 | 0 | } |
2593 | | |
2594 | | static Color GetConfBackgroundColor() |
2595 | 0 | { |
2596 | 0 | if (const ScTabViewShell* pTabViewShellBg = ScTabViewShell::GetActiveViewShell()) |
2597 | 0 | return pTabViewShellBg->GetViewRenderingData().GetDocColor(); |
2598 | 0 | return ScModule::get()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; |
2599 | 0 | } |
2600 | | |
2601 | | void ScOutputData::DrawEditParam::setPatternToEngine(bool bUseStyleColor) |
2602 | 0 | { |
2603 | | // syntax highlighting mode is ignored here |
2604 | | // StringDiffer doesn't look at hyphenate, language items |
2605 | |
|
2606 | 0 | if (ScPatternAttr::areSame(mpPattern, mpOldPattern) && mpCondSet == mpOldCondSet && mpTableSet == mpOldTableSet && mpPreviewFontSet == mpOldPreviewFontSet ) |
2607 | 0 | return; |
2608 | | |
2609 | 0 | Color nConfBackColor = GetConfBackgroundColor(); |
2610 | 0 | bool bCellContrast = bUseStyleColor && |
2611 | 0 | Application::GetSettings().GetStyleSettings().GetHighContrastMode(); |
2612 | |
|
2613 | 0 | SfxItemSet aSet( mpEngine->GetEmptyItemSet() ); |
2614 | 0 | mpPattern->FillEditItemSet( &aSet, mpCondSet, mpTableSet ); |
2615 | 0 | if ( mpPreviewFontSet ) |
2616 | 0 | { |
2617 | 0 | if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_FONT ) ) |
2618 | 0 | { |
2619 | | // tdf#125054 adapt WhichID |
2620 | 0 | aSet.PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO); |
2621 | 0 | } |
2622 | 0 | if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CJK_FONT ) ) |
2623 | 0 | { |
2624 | | // tdf#125054 adapt WhichID |
2625 | 0 | aSet.PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO_CJK); |
2626 | 0 | } |
2627 | 0 | if ( const SvxFontItem* pItem = mpPreviewFontSet->GetItemIfSet( ATTR_CTL_FONT ) ) |
2628 | 0 | { |
2629 | | // tdf#125054 adapt WhichID |
2630 | 0 | aSet.PutAsTargetWhich(*pItem, EE_CHAR_FONTINFO_CTL); |
2631 | 0 | } |
2632 | 0 | } |
2633 | 0 | bool bParaHyphenate = aSet.Get(EE_PARA_HYPHENATE).GetValue(); |
2634 | 0 | mpEngine->SetDefaults( std::move(aSet) ); |
2635 | 0 | mpOldPattern = mpPattern; |
2636 | 0 | mpOldCondSet = mpCondSet; |
2637 | 0 | mpOldTableSet = mpTableSet; |
2638 | 0 | mpOldPreviewFontSet = mpPreviewFontSet; |
2639 | |
|
2640 | 0 | EEControlBits nControl = mpEngine->GetControlWord(); |
2641 | 0 | if (meOrient == SvxCellOrientation::Stacked) |
2642 | 0 | nControl |= EEControlBits::ONECHARPERLINE; |
2643 | 0 | else |
2644 | 0 | nControl &= ~EEControlBits::ONECHARPERLINE; |
2645 | 0 | mpEngine->SetControlWord( nControl ); |
2646 | |
|
2647 | 0 | if ( !mbHyphenatorSet && bParaHyphenate ) |
2648 | 0 | { |
2649 | | // set hyphenator the first time it is needed |
2650 | 0 | css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); |
2651 | 0 | mpEngine->SetHyphenator( xXHyphenator ); |
2652 | 0 | mbHyphenatorSet = true; |
2653 | 0 | } |
2654 | |
|
2655 | 0 | Color aBackCol = mpPattern->GetItem( ATTR_BACKGROUND, mpCondSet ).GetColor(); |
2656 | 0 | if ( bUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) ) |
2657 | 0 | aBackCol = nConfBackColor; |
2658 | 0 | mpEngine->SetBackgroundColor( aBackCol ); |
2659 | 0 | } |
2660 | | |
2661 | | void ScOutputData::DrawEditParam::calcMargins(tools::Long& rTopM, tools::Long& rLeftM, tools::Long& rBottomM, tools::Long& rRightM, double nPPTX, double nPPTY) const |
2662 | 0 | { |
2663 | 0 | const SvxMarginItem& rMargin = mpPattern->GetItem(ATTR_MARGIN, mpCondSet); |
2664 | |
|
2665 | 0 | sal_uInt16 nIndent = 0; |
2666 | 0 | if (meHorJustAttr == SvxCellHorJustify::Left || meHorJustAttr == SvxCellHorJustify::Right) |
2667 | 0 | nIndent = lcl_GetValue<ScIndentItem, sal_uInt16>(*mpPattern, ATTR_INDENT, mpCondSet); |
2668 | |
|
2669 | 0 | rLeftM = static_cast<tools::Long>(((rMargin.GetLeftMargin() + nIndent) * nPPTX)); |
2670 | 0 | rTopM = static_cast<tools::Long>((rMargin.GetTopMargin() * nPPTY)); |
2671 | 0 | rRightM = static_cast<tools::Long>((rMargin.GetRightMargin() * nPPTX)); |
2672 | 0 | rBottomM = static_cast<tools::Long>((rMargin.GetBottomMargin() * nPPTY)); |
2673 | 0 | if(meHorJustAttr == SvxCellHorJustify::Right) |
2674 | 0 | { |
2675 | 0 | rLeftM = static_cast<tools::Long>((rMargin.GetLeftMargin() * nPPTX)); |
2676 | 0 | rRightM = static_cast<tools::Long>(((rMargin.GetRightMargin() + nIndent) * nPPTX)); |
2677 | 0 | } |
2678 | 0 | } |
2679 | | |
2680 | | void ScOutputData::DrawEditParam::calcPaperSize( |
2681 | | Size& rPaperSize, const tools::Rectangle& rAlignRect, double nPPTX, double nPPTY) const |
2682 | 0 | { |
2683 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
2684 | 0 | calcMargins(nTopM, nLeftM, nBottomM, nRightM, nPPTX, nPPTY); |
2685 | |
|
2686 | 0 | if (isVerticallyOriented()) |
2687 | 0 | { |
2688 | 0 | rPaperSize.setWidth( rAlignRect.GetHeight() - nTopM - nBottomM ); |
2689 | 0 | rPaperSize.setHeight( rAlignRect.GetWidth() - nLeftM - nRightM ); |
2690 | 0 | } |
2691 | 0 | else |
2692 | 0 | { |
2693 | 0 | rPaperSize.setWidth( rAlignRect.GetWidth() - nLeftM - nRightM ); |
2694 | 0 | rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM ); |
2695 | 0 | } |
2696 | |
|
2697 | 0 | if (mbAsianVertical) |
2698 | 0 | { |
2699 | 0 | rPaperSize.setHeight( rAlignRect.GetHeight() - nTopM - nBottomM ); |
2700 | | // Subtract some extra value from the height or else the text would go |
2701 | | // outside the cell area. The value of 5 is arbitrary, and is based |
2702 | | // entirely on heuristics. |
2703 | 0 | rPaperSize.AdjustHeight( -5 ); |
2704 | 0 | } |
2705 | 0 | } |
2706 | | |
2707 | | void ScOutputData::DrawEditParam::getEngineSize(ScFieldEditEngine* pEngine, tools::Long& rWidth, tools::Long& rHeight) const |
2708 | 0 | { |
2709 | 0 | tools::Long nEngineWidth = 0; |
2710 | 0 | if (!mbBreak || meOrient == SvxCellOrientation::Stacked || mbAsianVertical) |
2711 | 0 | nEngineWidth = static_cast<tools::Long>(pEngine->CalcTextWidth()); |
2712 | |
|
2713 | 0 | tools::Long nEngineHeight = pEngine->GetTextHeight(); |
2714 | |
|
2715 | 0 | if (isVerticallyOriented()) |
2716 | 0 | std::swap( nEngineWidth, nEngineHeight ); |
2717 | |
|
2718 | 0 | if (meOrient == SvxCellOrientation::Stacked) |
2719 | 0 | nEngineWidth = nEngineWidth * 11 / 10; |
2720 | |
|
2721 | 0 | rWidth = nEngineWidth; |
2722 | 0 | rHeight = nEngineHeight; |
2723 | 0 | } |
2724 | | |
2725 | | bool ScOutputData::DrawEditParam::hasLineBreak() const |
2726 | 0 | { |
2727 | 0 | return (mbBreak || (meOrient == SvxCellOrientation::Stacked) || mbAsianVertical); |
2728 | 0 | } |
2729 | | |
2730 | | bool ScOutputData::DrawEditParam::isHyperlinkCell() const |
2731 | 0 | { |
2732 | 0 | if (maCell.getType() != CELLTYPE_FORMULA) |
2733 | 0 | return false; |
2734 | | |
2735 | 0 | return maCell.getFormula()->IsHyperLinkCell(); |
2736 | 0 | } |
2737 | | |
2738 | | bool ScOutputData::DrawEditParam::isVerticallyOriented() const |
2739 | 0 | { |
2740 | 0 | return (meOrient == SvxCellOrientation::TopBottom || meOrient == SvxCellOrientation::BottomUp); |
2741 | 0 | } |
2742 | | |
2743 | | void ScOutputData::DrawEditParam::calcStartPosForVertical( |
2744 | | Point& rLogicStart, tools::Long nCellWidth, tools::Long nEngineWidth, tools::Long nTopM, const OutputDevice* pRefDevice) |
2745 | 0 | { |
2746 | 0 | OSL_ENSURE(isVerticallyOriented(), "Use this only for vertically oriented cell!"); |
2747 | |
|
2748 | 0 | if (mbPixelToLogic) |
2749 | 0 | rLogicStart = pRefDevice->PixelToLogic(rLogicStart); |
2750 | |
|
2751 | 0 | if (!mbBreak) |
2752 | 0 | return; |
2753 | | |
2754 | | // vertical adjustment is within the EditEngine |
2755 | 0 | if (mbPixelToLogic) |
2756 | 0 | rLogicStart.AdjustY(pRefDevice->PixelToLogic(Size(0,nTopM)).Height() ); |
2757 | 0 | else |
2758 | 0 | rLogicStart.AdjustY(nTopM ); |
2759 | |
|
2760 | 0 | switch (meHorJustResult) |
2761 | 0 | { |
2762 | 0 | case SvxCellHorJustify::Center: |
2763 | 0 | rLogicStart.AdjustX((nCellWidth - nEngineWidth) / 2 ); |
2764 | 0 | break; |
2765 | 0 | case SvxCellHorJustify::Right: |
2766 | 0 | rLogicStart.AdjustX(nCellWidth - nEngineWidth ); |
2767 | 0 | break; |
2768 | 0 | default: |
2769 | 0 | ; // do nothing |
2770 | 0 | } |
2771 | 0 | } |
2772 | | |
2773 | | void ScOutputData::DrawEditParam::setAlignmentToEngine() |
2774 | 0 | { |
2775 | 0 | if (isVerticallyOriented() || mbAsianVertical) |
2776 | 0 | { |
2777 | 0 | SvxAdjust eSvxAdjust = SvxAdjust::Left; |
2778 | 0 | switch (meVerJust) |
2779 | 0 | { |
2780 | 0 | case SvxCellVerJustify::Top: |
2781 | 0 | eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ? |
2782 | 0 | SvxAdjust::Left : SvxAdjust::Right; |
2783 | 0 | break; |
2784 | 0 | case SvxCellVerJustify::Center: |
2785 | 0 | eSvxAdjust = SvxAdjust::Center; |
2786 | 0 | break; |
2787 | 0 | case SvxCellVerJustify::Bottom: |
2788 | 0 | case SvxCellVerJustify::Standard: |
2789 | 0 | eSvxAdjust = (meOrient == SvxCellOrientation::TopBottom || mbAsianVertical) ? |
2790 | 0 | SvxAdjust::Right : SvxAdjust::Left; |
2791 | 0 | break; |
2792 | 0 | case SvxCellVerJustify::Block: |
2793 | 0 | eSvxAdjust = SvxAdjust::Block; |
2794 | 0 | break; |
2795 | 0 | } |
2796 | | |
2797 | 0 | mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) ); |
2798 | 0 | mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) ); |
2799 | |
|
2800 | 0 | if (meHorJustResult == SvxCellHorJustify::Block) |
2801 | 0 | mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) ); |
2802 | 0 | } |
2803 | 0 | else |
2804 | 0 | { |
2805 | | // horizontal alignment now may depend on cell content |
2806 | | // (for values with number formats with mixed script types) |
2807 | | // -> always set adjustment |
2808 | |
|
2809 | 0 | SvxAdjust eSvxAdjust = SvxAdjust::Left; |
2810 | 0 | if (meOrient == SvxCellOrientation::Stacked) |
2811 | 0 | eSvxAdjust = SvxAdjust::Center; |
2812 | 0 | else if (mbBreak) |
2813 | 0 | { |
2814 | 0 | if (meOrient == SvxCellOrientation::Standard) |
2815 | 0 | switch (meHorJustResult) |
2816 | 0 | { |
2817 | 0 | case SvxCellHorJustify::Repeat: // repeat is not yet implemented |
2818 | 0 | case SvxCellHorJustify::Standard: |
2819 | 0 | SAL_WARN("sc.ui","meHorJustResult does not match getAlignmentFromContext()"); |
2820 | 0 | [[fallthrough]]; |
2821 | 0 | case SvxCellHorJustify::Left: |
2822 | 0 | eSvxAdjust = SvxAdjust::Left; |
2823 | 0 | break; |
2824 | 0 | case SvxCellHorJustify::Center: |
2825 | 0 | eSvxAdjust = SvxAdjust::Center; |
2826 | 0 | break; |
2827 | 0 | case SvxCellHorJustify::Right: |
2828 | 0 | eSvxAdjust = SvxAdjust::Right; |
2829 | 0 | break; |
2830 | 0 | case SvxCellHorJustify::Block: |
2831 | 0 | eSvxAdjust = SvxAdjust::Block; |
2832 | 0 | break; |
2833 | 0 | } |
2834 | 0 | else |
2835 | 0 | switch (meVerJust) |
2836 | 0 | { |
2837 | 0 | case SvxCellVerJustify::Top: |
2838 | 0 | eSvxAdjust = SvxAdjust::Right; |
2839 | 0 | break; |
2840 | 0 | case SvxCellVerJustify::Center: |
2841 | 0 | eSvxAdjust = SvxAdjust::Center; |
2842 | 0 | break; |
2843 | 0 | case SvxCellVerJustify::Bottom: |
2844 | 0 | case SvxCellVerJustify::Standard: |
2845 | 0 | eSvxAdjust = SvxAdjust::Left; |
2846 | 0 | break; |
2847 | 0 | case SvxCellVerJustify::Block: |
2848 | 0 | eSvxAdjust = SvxAdjust::Block; |
2849 | 0 | break; |
2850 | 0 | } |
2851 | 0 | } |
2852 | | |
2853 | 0 | mpEngine->SetDefaultItem( SvxAdjustItem(eSvxAdjust, EE_PARA_JUST) ); |
2854 | |
|
2855 | 0 | if (mbAsianVertical) |
2856 | 0 | { |
2857 | 0 | mpEngine->SetDefaultItem( SvxJustifyMethodItem(meVerJustMethod, EE_PARA_JUST_METHOD) ); |
2858 | 0 | if (meHorJustResult == SvxCellHorJustify::Block) |
2859 | 0 | mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) ); |
2860 | 0 | } |
2861 | 0 | else |
2862 | 0 | { |
2863 | 0 | mpEngine->SetDefaultItem( SvxJustifyMethodItem(meHorJustMethod, EE_PARA_JUST_METHOD) ); |
2864 | 0 | if (meVerJust == SvxCellVerJustify::Block) |
2865 | 0 | mpEngine->SetDefaultItem( SvxVerJustifyItem(SvxCellVerJustify::Block, EE_PARA_VER_JUST) ); |
2866 | 0 | } |
2867 | 0 | } |
2868 | | |
2869 | 0 | mpEngine->SetVertical(mbAsianVertical); |
2870 | 0 | if (maCell.getType() == CELLTYPE_EDIT) |
2871 | 0 | { |
2872 | | // We need to synchronize the vertical mode in the EditTextObject |
2873 | | // instance too. No idea why we keep this state in two separate |
2874 | | // instances. |
2875 | 0 | const EditTextObject* pData = maCell.getEditText(); |
2876 | 0 | if (pData) |
2877 | 0 | const_cast<EditTextObject*>(pData)->SetVertical(mbAsianVertical); |
2878 | 0 | } |
2879 | 0 | } |
2880 | | |
2881 | | bool ScOutputData::DrawEditParam::adjustHorAlignment(ScFieldEditEngine* pEngine) |
2882 | 0 | { |
2883 | 0 | if (meHorJustResult == SvxCellHorJustify::Right || meHorJustResult == SvxCellHorJustify::Center) |
2884 | 0 | { |
2885 | 0 | SvxAdjust eEditAdjust = (meHorJustResult == SvxCellHorJustify::Center) ? |
2886 | 0 | SvxAdjust::Center : SvxAdjust::Right; |
2887 | |
|
2888 | 0 | const bool bPrevUpdateLayout = pEngine->SetUpdateLayout(false); |
2889 | 0 | pEngine->SetDefaultItem( SvxAdjustItem(eEditAdjust, EE_PARA_JUST) ); |
2890 | 0 | pEngine->SetUpdateLayout(bPrevUpdateLayout); |
2891 | 0 | return true; |
2892 | 0 | } |
2893 | 0 | return false; |
2894 | 0 | } |
2895 | | |
2896 | | void ScOutputData::DrawEditParam::adjustForHyperlinkInPDF(Point aURLStart, const OutputDevice* pDev) |
2897 | 0 | { |
2898 | | // PDF: whole-cell hyperlink from formula? |
2899 | 0 | vcl::PDFExtOutDevData* pPDFData = dynamic_cast<vcl::PDFExtOutDevData* >( pDev->GetExtOutDevData() ); |
2900 | 0 | bool bHasURL = pPDFData && isHyperlinkCell(); |
2901 | 0 | if (!bHasURL) |
2902 | 0 | return; |
2903 | | |
2904 | 0 | tools::Long nURLWidth = static_cast<tools::Long>(mpEngine->CalcTextWidth()); |
2905 | 0 | tools::Long nURLHeight = mpEngine->GetTextHeight(); |
2906 | 0 | if (mbBreak) |
2907 | 0 | { |
2908 | 0 | Size aPaper = mpEngine->GetPaperSize(); |
2909 | 0 | if ( mbAsianVertical ) |
2910 | 0 | nURLHeight = aPaper.Height(); |
2911 | 0 | else |
2912 | 0 | nURLWidth = aPaper.Width(); |
2913 | 0 | } |
2914 | 0 | if (isVerticallyOriented()) |
2915 | 0 | std::swap( nURLWidth, nURLHeight ); |
2916 | 0 | else if (mbAsianVertical) |
2917 | 0 | aURLStart.AdjustX( -nURLWidth ); |
2918 | |
|
2919 | 0 | tools::Rectangle aURLRect( aURLStart, Size( nURLWidth, nURLHeight ) ); |
2920 | 0 | lcl_DoHyperlinkResult(pDev, aURLRect, maCell); |
2921 | 0 | } |
2922 | | |
2923 | | // Returns true if the rect is clipped vertically |
2924 | | bool ScOutputData::AdjustAreaParamClipRect(OutputAreaParam& rAreaParam) |
2925 | 0 | { |
2926 | 0 | if( rAreaParam.maClipRect.Left() < mnScrX ) |
2927 | 0 | { |
2928 | 0 | rAreaParam.maClipRect.SetLeft( mnScrX ); |
2929 | 0 | rAreaParam.mbLeftClip = true; |
2930 | 0 | } |
2931 | 0 | if( rAreaParam.maClipRect.Right() > mnScrX + mnScrW ) |
2932 | 0 | { |
2933 | 0 | rAreaParam.maClipRect.SetRight( mnScrX + mnScrW ); //! minus one? |
2934 | 0 | rAreaParam.mbRightClip = true; |
2935 | 0 | } |
2936 | |
|
2937 | 0 | bool bVClip = false; |
2938 | |
|
2939 | 0 | if( rAreaParam.maClipRect.Top() < mnScrY ) |
2940 | 0 | { |
2941 | 0 | rAreaParam.maClipRect.SetTop( mnScrY ); |
2942 | 0 | bVClip = true; |
2943 | 0 | } |
2944 | 0 | if( rAreaParam.maClipRect.Bottom() > mnScrY + mnScrH ) |
2945 | 0 | { |
2946 | 0 | rAreaParam.maClipRect.SetBottom( mnScrY + mnScrH ); //! minus one? |
2947 | 0 | bVClip = true; |
2948 | 0 | } |
2949 | 0 | return bVClip; |
2950 | 0 | } |
2951 | | |
2952 | | // Doesn't handle clip marks - should be handled in advance using GetOutputArea |
2953 | | class ClearableClipRegion |
2954 | | { |
2955 | | public: |
2956 | | ClearableClipRegion( const tools::Rectangle& rRect, bool bClip, bool bSimClip, |
2957 | | const VclPtr<OutputDevice>& pDev, bool bMetaFile ) |
2958 | 0 | :mbMetaFile(bMetaFile) |
2959 | 0 | { |
2960 | 0 | if (!(bClip || bSimClip)) |
2961 | 0 | return; |
2962 | | |
2963 | 0 | maRect = rRect; |
2964 | 0 | if (bClip) // for bSimClip only initialize aClipRect |
2965 | 0 | { |
2966 | 0 | mpDev.reset(pDev); |
2967 | 0 | if (mbMetaFile) |
2968 | 0 | { |
2969 | 0 | mpDev->Push(); |
2970 | 0 | mpDev->IntersectClipRegion(maRect); |
2971 | 0 | } |
2972 | 0 | else |
2973 | 0 | mpDev->SetClipRegion(vcl::Region(maRect)); |
2974 | 0 | } |
2975 | 0 | } |
2976 | | |
2977 | | ~ClearableClipRegion() |
2978 | 0 | { |
2979 | | // Pop() or SetClipRegion() must only be called in case bClip was true |
2980 | | // in the ctor, and only then mpDev is set. |
2981 | 0 | if (mpDev) |
2982 | 0 | { |
2983 | 0 | if (mbMetaFile) |
2984 | 0 | mpDev->Pop(); |
2985 | 0 | else |
2986 | 0 | mpDev->SetClipRegion(); |
2987 | 0 | } |
2988 | 0 | } |
2989 | | |
2990 | 0 | const tools::Rectangle& getRect() const { return maRect; } |
2991 | | |
2992 | | private: |
2993 | | tools::Rectangle maRect; |
2994 | | VclPtr<OutputDevice> mpDev; |
2995 | | bool mbMetaFile; |
2996 | | }; |
2997 | | |
2998 | | // Returns needed width in current units; sets rNeededPixel to needed width in pixels |
2999 | | tools::Long ScOutputData::SetEngineTextAndGetWidth( DrawEditParam& rParam, const OUString& rSetString, |
3000 | | tools::Long& rNeededPixel, tools::Long nAddWidthPixels ) |
3001 | 0 | { |
3002 | 0 | rParam.mpEngine->SetTextCurrentDefaults( rSetString ); |
3003 | 0 | tools::Long nEngineWidth = static_cast<tools::Long>( rParam.mpEngine->CalcTextWidth() ); |
3004 | 0 | if ( rParam.mbPixelToLogic ) |
3005 | 0 | rNeededPixel = mpRefDevice->LogicToPixel( Size( nEngineWidth, 0 ) ).Width(); |
3006 | 0 | else |
3007 | 0 | rNeededPixel = nEngineWidth; |
3008 | |
|
3009 | 0 | rNeededPixel += nAddWidthPixels; |
3010 | |
|
3011 | 0 | return nEngineWidth; |
3012 | 0 | } |
3013 | | |
3014 | | void ScOutputData::DrawEditStandard(DrawEditParam& rParam) |
3015 | 0 | { |
3016 | 0 | OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard); |
3017 | 0 | OSL_ASSERT(!rParam.mbAsianVertical); |
3018 | |
|
3019 | 0 | Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1)); |
3020 | |
|
3021 | 0 | bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak); |
3022 | 0 | bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet); |
3023 | 0 | Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet); |
3024 | |
|
3025 | 0 | if ( rParam.meHorJustAttr == SvxCellHorJustify::Repeat ) |
3026 | 0 | { |
3027 | | // ignore orientation/rotation if "repeat" is active |
3028 | 0 | rParam.meOrient = SvxCellOrientation::Standard; |
3029 | 0 | nAttrRotate = 0_deg100; |
3030 | | |
3031 | | // #i31843# "repeat" with "line breaks" is treated as default alignment |
3032 | | // (but rotation is still disabled). |
3033 | | // Default again leads to context dependent alignment instead of |
3034 | | // SvxCellHorJustify::Standard. |
3035 | 0 | if ( rParam.mbBreak ) |
3036 | 0 | rParam.meHorJustResult = rParam.meHorJustContext; |
3037 | 0 | } |
3038 | |
|
3039 | 0 | if (nAttrRotate) |
3040 | 0 | { |
3041 | | //! set flag to find the cell in DrawRotated again ? |
3042 | | //! (or flag already set during DrawBackground, then no query here) |
3043 | 0 | return; // rotated is outputted separately |
3044 | 0 | } |
3045 | | |
3046 | 0 | SvxCellHorJustify eOutHorJust = rParam.meHorJustContext; |
3047 | | |
3048 | | //! mirror margin values for RTL? |
3049 | | //! move margin down to after final GetOutputArea call |
3050 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
3051 | 0 | rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY); |
3052 | |
|
3053 | 0 | SCCOL nXForPos = rParam.mnX; |
3054 | 0 | if ( nXForPos < mnX1 ) |
3055 | 0 | { |
3056 | 0 | nXForPos = mnX1; |
3057 | 0 | rParam.mnPosX = rParam.mnInitPosX; |
3058 | 0 | } |
3059 | 0 | SCSIZE nArrYForPos = rParam.mnArrY; |
3060 | 0 | if ( nArrYForPos < 1 ) |
3061 | 0 | { |
3062 | 0 | nArrYForPos = 1; |
3063 | 0 | rParam.mnPosY = mnScrY; |
3064 | 0 | } |
3065 | |
|
3066 | 0 | OutputAreaParam aAreaParam; |
3067 | | |
3068 | | // Initial page size - large for normal text, cell size for automatic line breaks |
3069 | |
|
3070 | 0 | Size aPaperSize( 1000000, 1000000 ); |
3071 | 0 | if (rParam.mbBreak) |
3072 | 0 | { |
3073 | | // call GetOutputArea with nNeeded=0, to get only the cell width |
3074 | | |
3075 | | //! handle nArrY == 0 |
3076 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0, |
3077 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3078 | 0 | rParam.mbCellIsValue, true, false, aAreaParam ); |
3079 | | |
3080 | | //! special ScEditUtil handling if formatting for printer |
3081 | 0 | rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY); |
3082 | 0 | } |
3083 | 0 | if (rParam.mbPixelToLogic) |
3084 | 0 | { |
3085 | 0 | Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize); |
3086 | 0 | if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice ) |
3087 | 0 | { |
3088 | | // #i85342# screen display and formatting for printer, |
3089 | | // use same GetEditArea call as in ScViewData::SetEditEngine |
3090 | |
|
3091 | 0 | double fFract(1.0); |
3092 | 0 | tools::Rectangle aUtilRect = ScEditUtil( *mpDoc, rParam.mnCellX, rParam.mnCellY, mnTab, Point(0,0), pFmtDevice, |
3093 | 0 | HMM_PER_TWIPS, HMM_PER_TWIPS, fFract, fFract ).GetEditArea( rParam.mpPattern, false ); |
3094 | 0 | aLogicSize.setWidth( aUtilRect.GetWidth() ); |
3095 | 0 | } |
3096 | 0 | rParam.mpEngine->SetPaperSize(aLogicSize); |
3097 | 0 | } |
3098 | 0 | else |
3099 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
3100 | | |
3101 | | // Fill the EditEngine (cell attributes and text) |
3102 | | |
3103 | | // default alignment for asian vertical mode is top-right |
3104 | 0 | if ( rParam.mbAsianVertical && rParam.meVerJust == SvxCellVerJustify::Standard ) |
3105 | 0 | rParam.meVerJust = SvxCellVerJustify::Top; |
3106 | |
|
3107 | 0 | rParam.setPatternToEngine(mbUseStyleColor); |
3108 | 0 | rParam.setAlignmentToEngine(); |
3109 | | // Don't format unnecessary parts if the text will be drawn from top (Standard will |
3110 | | // act that way if text doesn't fit, see below). |
3111 | 0 | rParam.mpEngine->EnableSkipOutsideFormat(rParam.meVerJust==SvxCellVerJustify::Top |
3112 | 0 | || rParam.meVerJust==SvxCellVerJustify::Standard); |
3113 | | |
3114 | | // Read content from cell |
3115 | |
|
3116 | 0 | bool bWrapFields = false; |
3117 | 0 | if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields)) |
3118 | | // Failed to read cell content. Bail out. |
3119 | 0 | return; |
3120 | | |
3121 | 0 | if ( mbSyntaxMode ) |
3122 | 0 | SetEditSyntaxColor(*rParam.mpEngine, rParam.maCell); |
3123 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
3124 | 0 | lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine |
3125 | |
|
3126 | 0 | rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
3127 | | |
3128 | | // Get final output area using the calculated width |
3129 | |
|
3130 | 0 | tools::Long nEngineWidth, nEngineHeight; |
3131 | 0 | rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight); |
3132 | |
|
3133 | 0 | tools::Long nNeededPixel = nEngineWidth; |
3134 | 0 | if (rParam.mbPixelToLogic) |
3135 | 0 | nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); |
3136 | 0 | nNeededPixel += nLeftM + nRightM; |
3137 | |
|
3138 | 0 | if (!rParam.mbBreak || bShrink) |
3139 | 0 | { |
3140 | | // for break, the first GetOutputArea call is sufficient |
3141 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel, |
3142 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3143 | 0 | rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam ); |
3144 | |
|
3145 | 0 | if ( bShrink ) |
3146 | 0 | { |
3147 | 0 | ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect, |
3148 | 0 | nLeftM, nTopM, nRightM, nBottomM, true, |
3149 | 0 | rParam.meOrient, 0_deg100, rParam.mbPixelToLogic, |
3150 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, |
3151 | 0 | aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
3152 | 0 | } |
3153 | 0 | if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 ) |
3154 | 0 | { |
3155 | | // First check if twice the space for the formatted text is available |
3156 | | // (otherwise just keep it unchanged). |
3157 | |
|
3158 | 0 | tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin |
3159 | 0 | tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM; |
3160 | 0 | if ( nAvailable >= 2 * nFormatted ) |
3161 | 0 | { |
3162 | | // "repeat" is handled with unformatted text (for performance reasons) |
3163 | 0 | OUString aCellStr = rParam.mpEngine->GetText(); |
3164 | |
|
3165 | 0 | tools::Long nRepeatSize = 0; |
3166 | 0 | SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 ); |
3167 | 0 | if ( pFmtDevice != mpRefDevice ) |
3168 | 0 | ++nRepeatSize; |
3169 | 0 | if ( nRepeatSize > 0 ) |
3170 | 0 | { |
3171 | 0 | tools::Long nRepeatCount = nAvailable / nRepeatSize; |
3172 | 0 | if ( nRepeatCount > 1 ) |
3173 | 0 | { |
3174 | 0 | OUStringBuffer aRepeated(aCellStr); |
3175 | 0 | for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) |
3176 | 0 | aRepeated.append(aCellStr); |
3177 | |
|
3178 | 0 | SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(), |
3179 | 0 | nNeededPixel, (nLeftM + nRightM ) ); |
3180 | |
|
3181 | 0 | nEngineHeight = rParam.mpEngine->GetTextHeight(); |
3182 | 0 | } |
3183 | 0 | } |
3184 | 0 | } |
3185 | 0 | } |
3186 | |
|
3187 | 0 | if (rParam.mnX >= mnX1 && rParam.mbCellIsValue |
3188 | 0 | && (aAreaParam.mbLeftClip || aAreaParam.mbRightClip)) |
3189 | 0 | { |
3190 | 0 | SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) ); |
3191 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
3192 | 0 | ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
3193 | 0 | SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign ); |
3194 | 0 | } |
3195 | |
|
3196 | 0 | if (eOutHorJust != SvxCellHorJustify::Left) |
3197 | 0 | { |
3198 | 0 | aPaperSize.setWidth( nNeededPixel + 1 ); |
3199 | 0 | if (rParam.mbPixelToLogic) |
3200 | 0 | rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
3201 | 0 | else |
3202 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
3203 | 0 | } |
3204 | 0 | } |
3205 | |
|
3206 | 0 | tools::Long nStartX = aAreaParam.maAlignRect.Left(); |
3207 | 0 | tools::Long nStartY = aAreaParam.maAlignRect.Top(); |
3208 | 0 | tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth(); |
3209 | 0 | tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; |
3210 | 0 | tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; |
3211 | |
|
3212 | 0 | if (rParam.mbBreak) |
3213 | 0 | { |
3214 | | // text with automatic breaks is aligned only within the |
3215 | | // edit engine's paper size, the output of the whole area |
3216 | | // is always left-aligned |
3217 | |
|
3218 | 0 | nStartX += nLeftM; |
3219 | 0 | } |
3220 | 0 | else |
3221 | 0 | { |
3222 | 0 | if ( eOutHorJust == SvxCellHorJustify::Right ) |
3223 | 0 | nStartX -= nNeededPixel - nCellWidth + nRightM + 1; |
3224 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Center ) |
3225 | 0 | nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2; |
3226 | 0 | else |
3227 | 0 | nStartX += nLeftM; |
3228 | 0 | } |
3229 | |
|
3230 | 0 | bool bOutside = (aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW); |
3231 | 0 | if (bOutside) |
3232 | 0 | return; |
3233 | | |
3234 | | // Also take fields in a cell with automatic breaks into account: clip to cell width |
3235 | 0 | bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields; |
3236 | 0 | bool bSimClip = false; |
3237 | |
|
3238 | 0 | Size aCellSize; // output area, excluding margins, in logical units |
3239 | 0 | if (rParam.mbPixelToLogic) |
3240 | 0 | aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); |
3241 | 0 | else |
3242 | 0 | aCellSize = Size( nOutWidth, nOutHeight ); |
3243 | |
|
3244 | 0 | if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() ) |
3245 | 0 | { |
3246 | 0 | const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE); |
3247 | 0 | bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; |
3248 | | |
3249 | | // Don't clip for text height when printing rows with optimal height, |
3250 | | // except when font size is from conditional formatting. |
3251 | | //! Allow clipping when vertically merged? |
3252 | 0 | if ( meType != OUTTYPE_PRINTER || |
3253 | 0 | ( mpDoc->GetRowFlags( rParam.mnCellY, mnTab ) & CRFlags::ManualSize ) || |
3254 | 0 | ( rParam.mpCondSet && SfxItemState::SET == |
3255 | 0 | rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) ) |
3256 | 0 | bClip = true; |
3257 | 0 | else |
3258 | 0 | bSimClip = true; |
3259 | | |
3260 | | // Show clip marks if height is at least 5pt too small and |
3261 | | // there are several lines of text. |
3262 | | // Not for asian vertical text, because that would interfere |
3263 | | // with the default right position of the text. |
3264 | | // Only with automatic line breaks, to avoid having to find |
3265 | | // the cells with the horizontal end of the text again. |
3266 | 0 | if ( nEngineHeight - aCellSize.Height() > 100 && |
3267 | 0 | rParam.mbBreak && mbMarkClipped && |
3268 | 0 | ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) ) |
3269 | 0 | { |
3270 | 0 | ScCellInfo* pClipMarkCell = nullptr; |
3271 | 0 | if ( bMerged ) |
3272 | 0 | { |
3273 | | // anywhere in the merged area... |
3274 | 0 | SCCOL nClipX = ( rParam.mnX < mnX1 ) ? mnX1 : rParam.mnX; |
3275 | 0 | pClipMarkCell = &mpRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX); |
3276 | 0 | } |
3277 | 0 | else |
3278 | 0 | pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
3279 | |
|
3280 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left? |
3281 | 0 | mbAnyClipped = true; |
3282 | |
|
3283 | 0 | tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX ); |
3284 | 0 | if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() ) |
3285 | 0 | aAreaParam.maClipRect.AdjustRight( -nMarkPixel ); |
3286 | | |
3287 | | // Standard is normally treated as Bottom, but if text height is clipped, then |
3288 | | // Top looks better and also allows using EditEngine::EnableSkipOutsideFormat(). |
3289 | 0 | if (rParam.meVerJust==SvxCellVerJustify::Standard) |
3290 | 0 | rParam.meVerJust=SvxCellVerJustify::Top; |
3291 | 0 | } |
3292 | 0 | } |
3293 | |
|
3294 | 0 | Point aURLStart; |
3295 | |
|
3296 | 0 | { // Clip marks are already handled in GetOutputArea |
3297 | 0 | ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect) |
3298 | 0 | : aAreaParam.maClipRect, bClip, bSimClip, mpDev, mbMetaFile); |
3299 | |
|
3300 | 0 | Point aLogicStart; |
3301 | 0 | if (rParam.mbPixelToLogic) |
3302 | 0 | aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) ); |
3303 | 0 | else |
3304 | 0 | aLogicStart = Point(nStartX, nStartY); |
3305 | |
|
3306 | 0 | if (!rParam.mbBreak) |
3307 | 0 | { |
3308 | | // horizontal alignment |
3309 | 0 | if (rParam.adjustHorAlignment(rParam.mpEngine)) |
3310 | | // reset adjustment for the next cell |
3311 | 0 | rParam.mpOldPattern = nullptr; |
3312 | 0 | } |
3313 | |
|
3314 | 0 | if (rParam.meVerJust==SvxCellVerJustify::Bottom || |
3315 | 0 | rParam.meVerJust==SvxCellVerJustify::Standard) |
3316 | 0 | { |
3317 | | //! if pRefDevice != pFmtDevice, keep heights in logic units, |
3318 | | //! only converting margin? |
3319 | |
|
3320 | 0 | if (rParam.mbPixelToLogic) |
3321 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + |
3322 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
3323 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() |
3324 | 0 | )).Height() ); |
3325 | 0 | else |
3326 | 0 | aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight ); |
3327 | 0 | } |
3328 | 0 | else if (rParam.meVerJust==SvxCellVerJustify::Center) |
3329 | 0 | { |
3330 | 0 | if (rParam.mbPixelToLogic) |
3331 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + ( |
3332 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
3333 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() ) |
3334 | 0 | / 2)).Height() ); |
3335 | 0 | else |
3336 | 0 | aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 ); |
3337 | 0 | } |
3338 | 0 | else // top |
3339 | 0 | { |
3340 | 0 | if (rParam.mbPixelToLogic) |
3341 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() ); |
3342 | 0 | else |
3343 | 0 | aLogicStart.AdjustY(nTopM ); |
3344 | 0 | } |
3345 | |
|
3346 | 0 | aURLStart = aLogicStart; // copy before modifying for orientation |
3347 | | |
3348 | | // bMoveClipped handling has been replaced by complete alignment |
3349 | | // handling (also extending to the left). |
3350 | |
|
3351 | 0 | if (bSimClip) |
3352 | 0 | { |
3353 | | // no hard clip, only draw the affected rows |
3354 | 0 | Point aDocStart = aClip.getRect().TopLeft(); |
3355 | 0 | aDocStart -= aLogicStart; |
3356 | 0 | rParam.mpEngine->DrawText_ToRectangle(*mpDev, aClip.getRect(), aDocStart, false); |
3357 | 0 | } |
3358 | 0 | else |
3359 | 0 | { |
3360 | 0 | rParam.mpEngine->DrawText_ToPosition(*mpDev, aLogicStart); |
3361 | 0 | } |
3362 | 0 | } |
3363 | |
|
3364 | 0 | rParam.adjustForHyperlinkInPDF(aURLStart, mpDev); |
3365 | 0 | } |
3366 | | |
3367 | | void ScOutputData::SetClipMarks( OutputAreaParam &aAreaParam, ScCellInfo* pClipMarkCell, |
3368 | | SvxCellHorJustify eOutHorJust, |
3369 | | tools::Long nLayoutSign ) |
3370 | 0 | { |
3371 | 0 | tools::Long nMarkPixel = SC_CLIPMARK_SIZE * mnPPTX; |
3372 | |
|
3373 | 0 | if ( eOutHorJust == SvxCellHorJustify::Left ) |
3374 | 0 | { |
3375 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Right; |
3376 | 0 | mbAnyClipped = true; |
3377 | 0 | aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) ); |
3378 | 0 | } |
3379 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Right ) |
3380 | 0 | { |
3381 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Left; |
3382 | 0 | mbAnyClipped = true; |
3383 | 0 | aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign ); |
3384 | 0 | } |
3385 | 0 | else |
3386 | 0 | { |
3387 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Right; |
3388 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Left; |
3389 | 0 | mbAnyClipped = true; |
3390 | 0 | aAreaParam.maClipRect.AdjustRight( -( nMarkPixel * nLayoutSign ) ); |
3391 | 0 | aAreaParam.maClipRect.AdjustLeft( nMarkPixel * nLayoutSign ); |
3392 | 0 | } |
3393 | |
|
3394 | 0 | } |
3395 | | |
3396 | | void ScOutputData::ShowClipMarks( DrawEditParam& rParam, tools::Long nEngineWidth, const Size& aCellSize, |
3397 | | bool bMerged, OutputAreaParam& aAreaParam, bool bTop) |
3398 | 0 | { |
3399 | | // Show clip marks if width is at least 5pt too small and |
3400 | | // there are several lines of text. |
3401 | | // Not for asian vertical text, because that would interfere |
3402 | | // with the default right position of the text. |
3403 | | // Only with automatic line breaks, to avoid having to find |
3404 | | // the cells with the horizontal end of the text again. |
3405 | 0 | if (nEngineWidth - aCellSize.Width() <= 100 || !rParam.mbBreak || !mbMarkClipped |
3406 | 0 | || (rParam.mpEngine->GetParagraphCount() <= 1 && rParam.mpEngine->GetLineCount(0) <= 1)) |
3407 | 0 | return; |
3408 | | |
3409 | 0 | ScCellInfo* pClipMarkCell = nullptr; |
3410 | 0 | if (bMerged) |
3411 | 0 | { |
3412 | | // anywhere in the merged area... |
3413 | 0 | SCCOL nClipX = (rParam.mnX < mnX1) ? mnX1 : rParam.mnX; |
3414 | 0 | pClipMarkCell = &mpRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX); |
3415 | 0 | } |
3416 | 0 | else |
3417 | 0 | pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
3418 | |
|
3419 | 0 | mbAnyClipped = true; |
3420 | 0 | mbVertical = true; |
3421 | 0 | const tools::Long nMarkPixel = static_cast<tools::Long>(SC_CLIPMARK_SIZE * mnPPTX); |
3422 | 0 | if (bTop) |
3423 | 0 | { |
3424 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Top; |
3425 | 0 | if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom()) |
3426 | 0 | aAreaParam.maClipRect.AdjustTop(+nMarkPixel); |
3427 | 0 | } |
3428 | 0 | else |
3429 | 0 | { |
3430 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Bottom; |
3431 | 0 | if (aAreaParam.maClipRect.Top() - nMarkPixel < aAreaParam.maClipRect.Bottom()) |
3432 | 0 | aAreaParam.maClipRect.AdjustBottom(-nMarkPixel); |
3433 | 0 | } |
3434 | 0 | } |
3435 | | |
3436 | | ClearableClipRegionPtr ScOutputData::Clip( DrawEditParam& rParam, const Size& aCellSize, |
3437 | | OutputAreaParam& aAreaParam, tools::Long nEngineWidth, |
3438 | | bool bWrapFields, bool bTop) |
3439 | 0 | { |
3440 | | // Also take fields in a cell with automatic breaks into account: clip to cell width |
3441 | 0 | bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields; |
3442 | 0 | bool bSimClip = false; |
3443 | |
|
3444 | 0 | const Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1)); |
3445 | 0 | if ( nEngineWidth >= aCellSize.Width() + aRefOne.Width() ) |
3446 | 0 | { |
3447 | 0 | const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE); |
3448 | 0 | const bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; |
3449 | | |
3450 | | // Don't clip for text height when printing rows with optimal height, |
3451 | | // except when font size is from conditional formatting. |
3452 | | //! Allow clipping when vertically merged? |
3453 | 0 | if ( meType != OUTTYPE_PRINTER || |
3454 | 0 | ( mpDoc->GetRowFlags( rParam.mnCellY, mnTab ) & CRFlags::ManualSize ) || |
3455 | 0 | ( rParam.mpCondSet && SfxItemState::SET == |
3456 | 0 | rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) ) |
3457 | 0 | bClip = true; |
3458 | 0 | else |
3459 | 0 | bSimClip = true; |
3460 | |
|
3461 | 0 | ShowClipMarks( rParam, nEngineWidth, aCellSize, bMerged, aAreaParam, bTop); |
3462 | 0 | } |
3463 | | |
3464 | | // Clip marks are already handled in GetOutputArea |
3465 | 0 | return ClearableClipRegionPtr(new ClearableClipRegion(rParam.mbPixelToLogic ? |
3466 | 0 | mpRefDevice->PixelToLogic(aAreaParam.maClipRect) |
3467 | 0 | : aAreaParam.maClipRect, bClip, bSimClip, mpDev, mbMetaFile)); |
3468 | 0 | } |
3469 | | |
3470 | | void ScOutputData::DrawEditBottomTop(DrawEditParam& rParam) |
3471 | 0 | { |
3472 | 0 | OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat); |
3473 | |
|
3474 | 0 | const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak); |
3475 | 0 | const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet); |
3476 | |
|
3477 | 0 | SvxCellHorJustify eOutHorJust = rParam.meHorJustContext; |
3478 | | |
3479 | | //! mirror margin values for RTL? |
3480 | | //! move margin down to after final GetOutputArea call |
3481 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
3482 | 0 | rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY); |
3483 | |
|
3484 | 0 | SCCOL nXForPos = rParam.mnX; |
3485 | 0 | if ( nXForPos < mnX1 ) |
3486 | 0 | { |
3487 | 0 | nXForPos = mnX1; |
3488 | 0 | rParam.mnPosX = rParam.mnInitPosX; |
3489 | 0 | } |
3490 | 0 | SCSIZE nArrYForPos = rParam.mnArrY; |
3491 | 0 | if ( nArrYForPos < 1 ) |
3492 | 0 | { |
3493 | 0 | nArrYForPos = 1; |
3494 | 0 | rParam.mnPosY = mnScrY; |
3495 | 0 | } |
3496 | |
|
3497 | 0 | OutputAreaParam aAreaParam; |
3498 | | |
3499 | | // Initial page size - large for normal text, cell size for automatic line breaks |
3500 | |
|
3501 | 0 | Size aPaperSize( 1000000, 1000000 ); |
3502 | 0 | if (rParam.mbBreak) |
3503 | 0 | { |
3504 | | // call GetOutputArea with nNeeded=0, to get only the cell width |
3505 | | |
3506 | | //! handle nArrY == 0 |
3507 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0, |
3508 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3509 | 0 | rParam.mbCellIsValue, true, false, aAreaParam ); |
3510 | | |
3511 | | //! special ScEditUtil handling if formatting for printer |
3512 | 0 | rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY); |
3513 | 0 | } |
3514 | 0 | if (rParam.mbPixelToLogic) |
3515 | 0 | { |
3516 | 0 | Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize); |
3517 | 0 | rParam.mpEngine->SetPaperSize(aLogicSize); |
3518 | 0 | } |
3519 | 0 | else |
3520 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
3521 | | |
3522 | | // Fill the EditEngine (cell attributes and text) |
3523 | |
|
3524 | 0 | rParam.setPatternToEngine(mbUseStyleColor); |
3525 | 0 | rParam.setAlignmentToEngine(); |
3526 | | |
3527 | | // Read content from cell |
3528 | |
|
3529 | 0 | bool bWrapFields = false; |
3530 | 0 | if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields)) |
3531 | | // Failed to read cell content. Bail out. |
3532 | 0 | return; |
3533 | | |
3534 | 0 | if ( mbSyntaxMode ) |
3535 | 0 | SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell ); |
3536 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
3537 | 0 | lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine |
3538 | |
|
3539 | 0 | rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
3540 | | |
3541 | | // Get final output area using the calculated width |
3542 | |
|
3543 | 0 | tools::Long nEngineWidth, nEngineHeight; |
3544 | 0 | rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight); |
3545 | |
|
3546 | 0 | tools::Long nNeededPixel = nEngineWidth; |
3547 | 0 | if (rParam.mbPixelToLogic) |
3548 | 0 | nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); |
3549 | 0 | nNeededPixel += nLeftM + nRightM; |
3550 | |
|
3551 | 0 | if (!rParam.mbBreak || bShrink) |
3552 | 0 | { |
3553 | | // for break, the first GetOutputArea call is sufficient |
3554 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel, |
3555 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3556 | 0 | rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam ); |
3557 | |
|
3558 | 0 | if ( bShrink ) |
3559 | 0 | { |
3560 | 0 | ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect, |
3561 | 0 | nLeftM, nTopM, nRightM, nBottomM, false, |
3562 | 0 | (rParam.meOrient), 0_deg100, rParam.mbPixelToLogic, |
3563 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, |
3564 | 0 | aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
3565 | 0 | } |
3566 | 0 | if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 ) |
3567 | 0 | { |
3568 | | // First check if twice the space for the formatted text is available |
3569 | | // (otherwise just keep it unchanged). |
3570 | |
|
3571 | 0 | const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin |
3572 | 0 | const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM; |
3573 | 0 | if ( nAvailable >= 2 * nFormatted ) |
3574 | 0 | { |
3575 | | // "repeat" is handled with unformatted text (for performance reasons) |
3576 | 0 | OUString aCellStr = rParam.mpEngine->GetText(); |
3577 | |
|
3578 | 0 | tools::Long nRepeatSize = 0; |
3579 | 0 | SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 ); |
3580 | 0 | if ( pFmtDevice != mpRefDevice ) |
3581 | 0 | ++nRepeatSize; |
3582 | 0 | if ( nRepeatSize > 0 ) |
3583 | 0 | { |
3584 | 0 | const tools::Long nRepeatCount = nAvailable / nRepeatSize; |
3585 | 0 | if ( nRepeatCount > 1 ) |
3586 | 0 | { |
3587 | 0 | OUStringBuffer aRepeated(aCellStr); |
3588 | 0 | for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) |
3589 | 0 | aRepeated.append(aCellStr); |
3590 | |
|
3591 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(), |
3592 | 0 | nNeededPixel, (nLeftM + nRightM ) ); |
3593 | |
|
3594 | 0 | nEngineHeight = rParam.mpEngine->GetTextHeight(); |
3595 | 0 | } |
3596 | 0 | } |
3597 | 0 | } |
3598 | 0 | } |
3599 | 0 | if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) |
3600 | 0 | { |
3601 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) ); |
3602 | | |
3603 | | // No clip marks if "###" doesn't fit (same as in DrawStrings) |
3604 | 0 | } |
3605 | 0 | } |
3606 | |
|
3607 | 0 | tools::Long nStartX = aAreaParam.maAlignRect.Left(); |
3608 | 0 | const tools::Long nStartY = aAreaParam.maAlignRect.Top(); |
3609 | 0 | const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth(); |
3610 | 0 | const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; |
3611 | 0 | const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; |
3612 | |
|
3613 | 0 | if (rParam.mbBreak) |
3614 | 0 | { |
3615 | | // text with automatic breaks is aligned only within the |
3616 | | // edit engine's paper size, the output of the whole area |
3617 | | // is always left-aligned |
3618 | |
|
3619 | 0 | nStartX += nLeftM; |
3620 | 0 | } |
3621 | 0 | else |
3622 | 0 | { |
3623 | 0 | if ( eOutHorJust == SvxCellHorJustify::Right ) |
3624 | 0 | nStartX -= nNeededPixel - nCellWidth + nRightM + 1; |
3625 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Center ) |
3626 | 0 | nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2; |
3627 | 0 | else |
3628 | 0 | nStartX += nLeftM; |
3629 | 0 | } |
3630 | |
|
3631 | 0 | const bool bOutside = (aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW); |
3632 | 0 | if (bOutside) |
3633 | 0 | return; |
3634 | | |
3635 | | // output area, excluding margins, in logical units |
3636 | 0 | const Size aCellSize = rParam.mbPixelToLogic |
3637 | 0 | ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ) |
3638 | 0 | : Size( nOutWidth, nOutHeight ); |
3639 | |
|
3640 | 0 | Point aURLStart; |
3641 | |
|
3642 | 0 | { |
3643 | 0 | const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, true ); |
3644 | |
|
3645 | 0 | Point aLogicStart(nStartX, nStartY); |
3646 | 0 | rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice); |
3647 | |
|
3648 | 0 | aURLStart = aLogicStart; // copy before modifying for orientation |
3649 | |
|
3650 | 0 | if (rParam.meHorJustResult == SvxCellHorJustify::Block || rParam.mbBreak) |
3651 | 0 | { |
3652 | 0 | Size aPSize = rParam.mpEngine->GetPaperSize(); |
3653 | 0 | aPSize.setWidth( aCellSize.Height() ); |
3654 | 0 | rParam.mpEngine->SetPaperSize(aPSize); |
3655 | 0 | aLogicStart.AdjustY( |
3656 | 0 | rParam.mbBreak ? aPSize.Width() : nEngineHeight ); |
3657 | 0 | } |
3658 | 0 | else |
3659 | 0 | { |
3660 | | // Note that the "paper" is rotated 90 degrees to the left, so |
3661 | | // paper's width is in vertical direction. Also, the whole text |
3662 | | // is on a single line, as text wrap is not in effect. |
3663 | | |
3664 | | // Set the paper width to be the width of the text. |
3665 | 0 | Size aPSize = rParam.mpEngine->GetPaperSize(); |
3666 | 0 | aPSize.setWidth( rParam.mpEngine->CalcTextWidth() ); |
3667 | 0 | rParam.mpEngine->SetPaperSize(aPSize); |
3668 | |
|
3669 | 0 | tools::Long nGap = 0; |
3670 | 0 | tools::Long nTopOffset = 0; |
3671 | 0 | if (rParam.mbPixelToLogic) |
3672 | 0 | { |
3673 | 0 | nGap = mpRefDevice->LogicToPixel(aCellSize).Height() - mpRefDevice->LogicToPixel(aPSize).Width(); |
3674 | 0 | nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height(); |
3675 | 0 | nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height(); |
3676 | 0 | } |
3677 | 0 | else |
3678 | 0 | { |
3679 | 0 | nGap = aCellSize.Height() - aPSize.Width(); |
3680 | 0 | nTopOffset = nTopM; |
3681 | 0 | } |
3682 | | |
3683 | | // First, align text to bottom. |
3684 | 0 | aLogicStart.AdjustY(aCellSize.Height() ); |
3685 | 0 | aLogicStart.AdjustY(nTopOffset ); |
3686 | |
|
3687 | 0 | switch (rParam.meVerJust) |
3688 | 0 | { |
3689 | 0 | case SvxCellVerJustify::Standard: |
3690 | 0 | case SvxCellVerJustify::Bottom: |
3691 | | // align to bottom (do nothing). |
3692 | 0 | break; |
3693 | 0 | case SvxCellVerJustify::Center: |
3694 | | // center it. |
3695 | 0 | aLogicStart.AdjustY( -(nGap / 2) ); |
3696 | 0 | break; |
3697 | 0 | case SvxCellVerJustify::Block: |
3698 | 0 | case SvxCellVerJustify::Top: |
3699 | | // align to top |
3700 | 0 | aLogicStart.AdjustY( -nGap ); |
3701 | 0 | break; |
3702 | 0 | default: |
3703 | 0 | ; |
3704 | 0 | } |
3705 | 0 | } |
3706 | | |
3707 | 0 | rParam.mpEngine->DrawText_ToPosition(*mpDev, aLogicStart, 900_deg10); |
3708 | 0 | } |
3709 | | |
3710 | 0 | rParam.adjustForHyperlinkInPDF(aURLStart, mpDev); |
3711 | 0 | } |
3712 | | |
3713 | | void ScOutputData::DrawEditTopBottom(DrawEditParam& rParam) |
3714 | 0 | { |
3715 | 0 | OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat); |
3716 | |
|
3717 | 0 | const bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak); |
3718 | 0 | const bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet); |
3719 | |
|
3720 | 0 | SvxCellHorJustify eOutHorJust = rParam.meHorJustContext; |
3721 | | |
3722 | | //! mirror margin values for RTL? |
3723 | | //! move margin down to after final GetOutputArea call |
3724 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
3725 | 0 | rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY); |
3726 | |
|
3727 | 0 | SCCOL nXForPos = rParam.mnX; |
3728 | 0 | if ( nXForPos < mnX1 ) |
3729 | 0 | { |
3730 | 0 | nXForPos = mnX1; |
3731 | 0 | rParam.mnPosX = rParam.mnInitPosX; |
3732 | 0 | } |
3733 | 0 | SCSIZE nArrYForPos = rParam.mnArrY; |
3734 | 0 | if ( nArrYForPos < 1 ) |
3735 | 0 | { |
3736 | 0 | nArrYForPos = 1; |
3737 | 0 | rParam.mnPosY = mnScrY; |
3738 | 0 | } |
3739 | |
|
3740 | 0 | OutputAreaParam aAreaParam; |
3741 | | |
3742 | | // Initial page size - large for normal text, cell size for automatic line breaks |
3743 | |
|
3744 | 0 | Size aPaperSize( 1000000, 1000000 ); |
3745 | 0 | if (rParam.hasLineBreak()) |
3746 | 0 | { |
3747 | | // call GetOutputArea with nNeeded=0, to get only the cell width |
3748 | | |
3749 | | //! handle nArrY == 0 |
3750 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0, |
3751 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3752 | 0 | rParam.mbCellIsValue, true, false, aAreaParam ); |
3753 | | |
3754 | | //! special ScEditUtil handling if formatting for printer |
3755 | 0 | rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY); |
3756 | 0 | } |
3757 | 0 | if (rParam.mbPixelToLogic) |
3758 | 0 | { |
3759 | 0 | Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize); |
3760 | 0 | rParam.mpEngine->SetPaperSize(aLogicSize); |
3761 | 0 | } |
3762 | 0 | else |
3763 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
3764 | | |
3765 | | // Fill the EditEngine (cell attributes and text) |
3766 | |
|
3767 | 0 | rParam.setPatternToEngine(mbUseStyleColor); |
3768 | 0 | rParam.setAlignmentToEngine(); |
3769 | | |
3770 | | // Read content from cell |
3771 | |
|
3772 | 0 | bool bWrapFields = false; |
3773 | 0 | if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields)) |
3774 | | // Failed to read cell content. Bail out. |
3775 | 0 | return; |
3776 | | |
3777 | 0 | if ( mbSyntaxMode ) |
3778 | 0 | SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell ); |
3779 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
3780 | 0 | lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine |
3781 | |
|
3782 | 0 | rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
3783 | | |
3784 | | // Get final output area using the calculated width |
3785 | |
|
3786 | 0 | tools::Long nEngineWidth, nEngineHeight; |
3787 | 0 | rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight); |
3788 | |
|
3789 | 0 | tools::Long nNeededPixel = nEngineWidth; |
3790 | 0 | if (rParam.mbPixelToLogic) |
3791 | 0 | nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); |
3792 | 0 | nNeededPixel += nLeftM + nRightM; |
3793 | |
|
3794 | 0 | if (!rParam.mbBreak || bShrink) |
3795 | 0 | { |
3796 | | // for break, the first GetOutputArea call is sufficient |
3797 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel, |
3798 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3799 | 0 | rParam.mbCellIsValue || bRepeat || bShrink, false, false, aAreaParam ); |
3800 | |
|
3801 | 0 | if ( bShrink ) |
3802 | 0 | { |
3803 | 0 | ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect, |
3804 | 0 | nLeftM, nTopM, nRightM, nBottomM, false, |
3805 | 0 | rParam.meOrient, 0_deg100, rParam.mbPixelToLogic, |
3806 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, |
3807 | 0 | aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
3808 | 0 | } |
3809 | 0 | if ( bRepeat && !aAreaParam.mbLeftClip && !aAreaParam.mbRightClip && rParam.mpEngine->GetParagraphCount() == 1 ) |
3810 | 0 | { |
3811 | | // First check if twice the space for the formatted text is available |
3812 | | // (otherwise just keep it unchanged). |
3813 | |
|
3814 | 0 | const tools::Long nFormatted = nNeededPixel - nLeftM - nRightM; // without margin |
3815 | 0 | const tools::Long nAvailable = aAreaParam.maAlignRect.GetWidth() - nLeftM - nRightM; |
3816 | 0 | if ( nAvailable >= 2 * nFormatted ) |
3817 | 0 | { |
3818 | | // "repeat" is handled with unformatted text (for performance reasons) |
3819 | 0 | OUString aCellStr = rParam.mpEngine->GetText(); |
3820 | |
|
3821 | 0 | tools::Long nRepeatSize = 0; |
3822 | 0 | SetEngineTextAndGetWidth( rParam, aCellStr, nRepeatSize, 0 ); |
3823 | |
|
3824 | 0 | if ( pFmtDevice != mpRefDevice ) |
3825 | 0 | ++nRepeatSize; |
3826 | 0 | if ( nRepeatSize > 0 ) |
3827 | 0 | { |
3828 | 0 | const tools::Long nRepeatCount = nAvailable / nRepeatSize; |
3829 | 0 | if ( nRepeatCount > 1 ) |
3830 | 0 | { |
3831 | 0 | OUStringBuffer aRepeated(aCellStr); |
3832 | 0 | for ( tools::Long nRepeat = 1; nRepeat < nRepeatCount; nRepeat++ ) |
3833 | 0 | aRepeated.append(aCellStr); |
3834 | |
|
3835 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, aRepeated.makeStringAndClear(), |
3836 | 0 | nNeededPixel, (nLeftM + nRightM ) ); |
3837 | |
|
3838 | 0 | nEngineHeight = rParam.mpEngine->GetTextHeight(); |
3839 | 0 | } |
3840 | 0 | } |
3841 | 0 | } |
3842 | 0 | } |
3843 | 0 | if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) |
3844 | 0 | { |
3845 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) ); |
3846 | | |
3847 | | // No clip marks if "###" doesn't fit (same as in DrawStrings) |
3848 | 0 | } |
3849 | 0 | } |
3850 | |
|
3851 | 0 | tools::Long nStartX = aAreaParam.maAlignRect.Left(); |
3852 | 0 | const tools::Long nStartY = aAreaParam.maAlignRect.Top(); |
3853 | 0 | const tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth(); |
3854 | 0 | const tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; |
3855 | 0 | const tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; |
3856 | |
|
3857 | 0 | if (rParam.mbBreak) |
3858 | 0 | { |
3859 | | // text with automatic breaks is aligned only within the |
3860 | | // edit engine's paper size, the output of the whole area |
3861 | | // is always left-aligned |
3862 | |
|
3863 | 0 | nStartX += nLeftM; |
3864 | 0 | if (rParam.meHorJustResult == SvxCellHorJustify::Block) |
3865 | 0 | nStartX += aPaperSize.Height(); |
3866 | 0 | } |
3867 | 0 | else |
3868 | 0 | { |
3869 | 0 | if ( eOutHorJust == SvxCellHorJustify::Right ) |
3870 | 0 | nStartX -= nNeededPixel - nCellWidth + nRightM + 1; |
3871 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Center ) |
3872 | 0 | nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2; |
3873 | 0 | else |
3874 | 0 | nStartX += nLeftM; |
3875 | 0 | } |
3876 | |
|
3877 | 0 | const bool bOutside = (aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW); |
3878 | 0 | if (bOutside) |
3879 | 0 | return; |
3880 | | |
3881 | | // output area, excluding margins, in logical units |
3882 | 0 | const Size aCellSize = rParam.mbPixelToLogic |
3883 | 0 | ? mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ) |
3884 | 0 | : Size( nOutWidth, nOutHeight ); |
3885 | |
|
3886 | 0 | Point aURLStart; |
3887 | |
|
3888 | 0 | { |
3889 | 0 | const auto pClipRegion = Clip( rParam, aCellSize, aAreaParam, nEngineWidth, bWrapFields, false ); |
3890 | |
|
3891 | 0 | Point aLogicStart(nStartX, nStartY); |
3892 | 0 | rParam.calcStartPosForVertical(aLogicStart, aCellSize.Width(), nEngineWidth, nTopM, mpRefDevice); |
3893 | |
|
3894 | 0 | aURLStart = aLogicStart; // copy before modifying for orientation |
3895 | |
|
3896 | 0 | if (rParam.meHorJustResult != SvxCellHorJustify::Block) |
3897 | 0 | { |
3898 | 0 | aLogicStart.AdjustX(nEngineWidth ); |
3899 | 0 | if (!rParam.mbBreak) |
3900 | 0 | { |
3901 | | // Set the paper width to text size. |
3902 | 0 | Size aPSize = rParam.mpEngine->GetPaperSize(); |
3903 | 0 | aPSize.setWidth( rParam.mpEngine->CalcTextWidth() ); |
3904 | 0 | rParam.mpEngine->SetPaperSize(aPSize); |
3905 | |
|
3906 | 0 | tools::Long nGap = 0; |
3907 | 0 | tools::Long nTopOffset = 0; // offset by top margin |
3908 | 0 | if (rParam.mbPixelToLogic) |
3909 | 0 | { |
3910 | 0 | nGap = mpRefDevice->LogicToPixel(aPSize).Width() - mpRefDevice->LogicToPixel(aCellSize).Height(); |
3911 | 0 | nGap = mpRefDevice->PixelToLogic(Size(0, nGap)).Height(); |
3912 | 0 | nTopOffset = mpRefDevice->PixelToLogic(Size(0,nTopM)).Height(); |
3913 | 0 | } |
3914 | 0 | else |
3915 | 0 | { |
3916 | 0 | nGap = aPSize.Width() - aCellSize.Height(); |
3917 | 0 | nTopOffset = nTopM; |
3918 | 0 | } |
3919 | 0 | aLogicStart.AdjustY(nTopOffset ); |
3920 | |
|
3921 | 0 | switch (rParam.meVerJust) |
3922 | 0 | { |
3923 | 0 | case SvxCellVerJustify::Standard: |
3924 | 0 | case SvxCellVerJustify::Bottom: |
3925 | | // align to bottom |
3926 | 0 | aLogicStart.AdjustY( -nGap ); |
3927 | 0 | break; |
3928 | 0 | case SvxCellVerJustify::Center: |
3929 | | // center it. |
3930 | 0 | aLogicStart.AdjustY( -(nGap / 2) ); |
3931 | 0 | break; |
3932 | 0 | case SvxCellVerJustify::Block: |
3933 | 0 | case SvxCellVerJustify::Top: |
3934 | | // align to top (do nothing) |
3935 | 0 | default: |
3936 | 0 | ; |
3937 | 0 | } |
3938 | 0 | } |
3939 | 0 | } |
3940 | | |
3941 | | // bMoveClipped handling has been replaced by complete alignment |
3942 | | // handling (also extending to the left). |
3943 | | |
3944 | 0 | rParam.mpEngine->DrawText_ToPosition(*mpDev, aLogicStart, 2700_deg10); |
3945 | 0 | } |
3946 | | |
3947 | 0 | rParam.adjustForHyperlinkInPDF(aURLStart, mpDev); |
3948 | 0 | } |
3949 | | |
3950 | | void ScOutputData::DrawEditStacked(DrawEditParam& rParam) |
3951 | 0 | { |
3952 | 0 | OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat); |
3953 | 0 | Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1)); |
3954 | |
|
3955 | 0 | bool bRepeat = (rParam.meHorJustAttr == SvxCellHorJustify::Repeat && !rParam.mbBreak); |
3956 | 0 | bool bShrink = !rParam.mbBreak && !bRepeat && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet); |
3957 | |
|
3958 | 0 | rParam.mbAsianVertical = |
3959 | 0 | lcl_GetBoolValue(*rParam.mpPattern, ATTR_VERTICAL_ASIAN, rParam.mpCondSet); |
3960 | |
|
3961 | 0 | if ( rParam.mbAsianVertical ) |
3962 | 0 | { |
3963 | | // in asian mode, use EditEngine::SetVertical instead of EEControlBits::ONECHARPERLINE |
3964 | 0 | rParam.meOrient = SvxCellOrientation::Standard; |
3965 | 0 | DrawEditAsianVertical(rParam); |
3966 | 0 | return; |
3967 | 0 | } |
3968 | | |
3969 | 0 | SvxCellHorJustify eOutHorJust = rParam.meHorJustContext; |
3970 | | |
3971 | | //! mirror margin values for RTL? |
3972 | | //! move margin down to after final GetOutputArea call |
3973 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
3974 | 0 | rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY); |
3975 | |
|
3976 | 0 | SCCOL nXForPos = rParam.mnX; |
3977 | 0 | if ( nXForPos < mnX1 ) |
3978 | 0 | { |
3979 | 0 | nXForPos = mnX1; |
3980 | 0 | rParam.mnPosX = rParam.mnInitPosX; |
3981 | 0 | } |
3982 | 0 | SCSIZE nArrYForPos = rParam.mnArrY; |
3983 | 0 | if ( nArrYForPos < 1 ) |
3984 | 0 | { |
3985 | 0 | nArrYForPos = 1; |
3986 | 0 | rParam.mnPosY = mnScrY; |
3987 | 0 | } |
3988 | |
|
3989 | 0 | OutputAreaParam aAreaParam; |
3990 | | |
3991 | | // Initial page size - large for normal text, cell size for automatic line breaks |
3992 | |
|
3993 | 0 | Size aPaperSize( 1000000, 1000000 ); |
3994 | | // call GetOutputArea with nNeeded=0, to get only the cell width |
3995 | | |
3996 | | //! handle nArrY == 0 |
3997 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0, |
3998 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
3999 | 0 | rParam.mbCellIsValue, true, false, aAreaParam ); |
4000 | | |
4001 | | //! special ScEditUtil handling if formatting for printer |
4002 | 0 | rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY); |
4003 | |
|
4004 | 0 | if (rParam.mbPixelToLogic) |
4005 | 0 | { |
4006 | 0 | Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize); |
4007 | 0 | if ( rParam.mbBreak && mpRefDevice != pFmtDevice ) |
4008 | 0 | { |
4009 | | // #i85342# screen display and formatting for printer, |
4010 | | // use same GetEditArea call as in ScViewData::SetEditEngine |
4011 | |
|
4012 | 0 | double fFract(1.0); |
4013 | 0 | tools::Rectangle aUtilRect = ScEditUtil( *mpDoc, rParam.mnCellX, rParam.mnCellY, mnTab, Point(0,0), pFmtDevice, |
4014 | 0 | HMM_PER_TWIPS, HMM_PER_TWIPS, fFract, fFract ).GetEditArea( rParam.mpPattern, false ); |
4015 | 0 | aLogicSize.setWidth( aUtilRect.GetWidth() ); |
4016 | 0 | } |
4017 | 0 | rParam.mpEngine->SetPaperSize(aLogicSize); |
4018 | 0 | } |
4019 | 0 | else |
4020 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
4021 | | |
4022 | | // Fill the EditEngine (cell attributes and text) |
4023 | |
|
4024 | 0 | rParam.setPatternToEngine(mbUseStyleColor); |
4025 | 0 | rParam.setAlignmentToEngine(); |
4026 | | |
4027 | | // Read content from cell |
4028 | |
|
4029 | 0 | bool bWrapFields = false; |
4030 | 0 | if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields)) |
4031 | | // Failed to read cell content. Bail out. |
4032 | 0 | return; |
4033 | | |
4034 | 0 | if ( mbSyntaxMode ) |
4035 | 0 | SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell ); |
4036 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
4037 | 0 | lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine |
4038 | |
|
4039 | 0 | rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
4040 | | |
4041 | | // Get final output area using the calculated width |
4042 | |
|
4043 | 0 | tools::Long nEngineWidth, nEngineHeight; |
4044 | 0 | rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight); |
4045 | |
|
4046 | 0 | tools::Long nNeededPixel = nEngineWidth; |
4047 | 0 | if (rParam.mbPixelToLogic) |
4048 | 0 | nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); |
4049 | 0 | nNeededPixel += nLeftM + nRightM; |
4050 | |
|
4051 | 0 | if (bShrink) |
4052 | 0 | { |
4053 | | // for break, the first GetOutputArea call is sufficient |
4054 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel, |
4055 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
4056 | 0 | true, false, false, aAreaParam ); |
4057 | |
|
4058 | 0 | ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect, |
4059 | 0 | nLeftM, nTopM, nRightM, nBottomM, true, |
4060 | 0 | rParam.meOrient, 0_deg100, rParam.mbPixelToLogic, |
4061 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, |
4062 | 0 | aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
4063 | |
|
4064 | 0 | if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) |
4065 | 0 | { |
4066 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) ); |
4067 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
4068 | 0 | ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
4069 | 0 | SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign ); |
4070 | 0 | } |
4071 | |
|
4072 | 0 | if ( eOutHorJust != SvxCellHorJustify::Left ) |
4073 | 0 | { |
4074 | 0 | aPaperSize.setWidth( nNeededPixel + 1 ); |
4075 | 0 | if (rParam.mbPixelToLogic) |
4076 | 0 | rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
4077 | 0 | else |
4078 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
4079 | 0 | } |
4080 | 0 | } |
4081 | |
|
4082 | 0 | tools::Long nStartX = aAreaParam.maAlignRect.Left(); |
4083 | 0 | tools::Long nStartY = aAreaParam.maAlignRect.Top(); |
4084 | 0 | tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth(); |
4085 | 0 | tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; |
4086 | 0 | tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; |
4087 | |
|
4088 | 0 | if (rParam.mbBreak) |
4089 | 0 | { |
4090 | | // text with automatic breaks is aligned only within the |
4091 | | // edit engine's paper size, the output of the whole area |
4092 | | // is always left-aligned |
4093 | |
|
4094 | 0 | nStartX += nLeftM; |
4095 | 0 | } |
4096 | 0 | else |
4097 | 0 | { |
4098 | 0 | if ( eOutHorJust == SvxCellHorJustify::Right ) |
4099 | 0 | nStartX -= nNeededPixel - nCellWidth + nRightM + 1; |
4100 | 0 | else if ( eOutHorJust == SvxCellHorJustify::Center ) |
4101 | 0 | nStartX -= ( nNeededPixel - nCellWidth + nRightM + 1 - nLeftM ) / 2; |
4102 | 0 | else |
4103 | 0 | nStartX += nLeftM; |
4104 | 0 | } |
4105 | |
|
4106 | 0 | bool bOutside = (aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW); |
4107 | 0 | if (bOutside) |
4108 | 0 | return; |
4109 | | |
4110 | | // Also take fields in a cell with automatic breaks into account: clip to cell width |
4111 | 0 | bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields; |
4112 | 0 | bool bSimClip = false; |
4113 | |
|
4114 | 0 | Size aCellSize; // output area, excluding margins, in logical units |
4115 | 0 | if (rParam.mbPixelToLogic) |
4116 | 0 | aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); |
4117 | 0 | else |
4118 | 0 | aCellSize = Size( nOutWidth, nOutHeight ); |
4119 | |
|
4120 | 0 | if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() ) |
4121 | 0 | { |
4122 | 0 | const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE); |
4123 | 0 | bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; |
4124 | | |
4125 | | // Don't clip for text height when printing rows with optimal height, |
4126 | | // except when font size is from conditional formatting. |
4127 | | //! Allow clipping when vertically merged? |
4128 | 0 | if ( meType != OUTTYPE_PRINTER || |
4129 | 0 | ( mpDoc->GetRowFlags( rParam.mnCellY, mnTab ) & CRFlags::ManualSize ) || |
4130 | 0 | ( rParam.mpCondSet && SfxItemState::SET == |
4131 | 0 | rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) ) |
4132 | 0 | bClip = true; |
4133 | 0 | else |
4134 | 0 | bSimClip = true; |
4135 | | |
4136 | | // Show clip marks if height is at least 5pt too small and |
4137 | | // there are several lines of text. |
4138 | | // Not for asian vertical text, because that would interfere |
4139 | | // with the default right position of the text. |
4140 | | // Only with automatic line breaks, to avoid having to find |
4141 | | // the cells with the horizontal end of the text again. |
4142 | 0 | if ( nEngineHeight - aCellSize.Height() > 100 && |
4143 | 0 | rParam.mbBreak && mbMarkClipped && |
4144 | 0 | ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) ) |
4145 | 0 | { |
4146 | 0 | ScCellInfo* pClipMarkCell = nullptr; |
4147 | 0 | if ( bMerged ) |
4148 | 0 | { |
4149 | | // anywhere in the merged area... |
4150 | 0 | SCCOL nClipX = ( rParam.mnX < mnX1 ) ? mnX1 : rParam.mnX; |
4151 | 0 | pClipMarkCell = &mpRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX); |
4152 | 0 | } |
4153 | 0 | else |
4154 | 0 | pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
4155 | |
|
4156 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left? |
4157 | 0 | mbAnyClipped = true; |
4158 | |
|
4159 | 0 | tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX ); |
4160 | 0 | if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() ) |
4161 | 0 | aAreaParam.maClipRect.AdjustRight( -nMarkPixel ); |
4162 | 0 | } |
4163 | 0 | } |
4164 | |
|
4165 | 0 | Point aURLStart; |
4166 | |
|
4167 | 0 | { // Clip marks are already handled in GetOutputArea |
4168 | 0 | ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect) |
4169 | 0 | : aAreaParam.maClipRect, bClip, bSimClip, mpDev, mbMetaFile); |
4170 | |
|
4171 | 0 | Point aLogicStart; |
4172 | 0 | if (rParam.mbPixelToLogic) |
4173 | 0 | aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) ); |
4174 | 0 | else |
4175 | 0 | aLogicStart = Point(nStartX, nStartY); |
4176 | |
|
4177 | 0 | if (rParam.meVerJust==SvxCellVerJustify::Bottom || |
4178 | 0 | rParam.meVerJust==SvxCellVerJustify::Standard) |
4179 | 0 | { |
4180 | | //! if pRefDevice != pFmtDevice, keep heights in logic units, |
4181 | | //! only converting margin? |
4182 | |
|
4183 | 0 | if (rParam.mbPixelToLogic) |
4184 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + |
4185 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
4186 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() |
4187 | 0 | )).Height() ); |
4188 | 0 | else |
4189 | 0 | aLogicStart.AdjustY(nTopM + aCellSize.Height() - nEngineHeight ); |
4190 | 0 | } |
4191 | 0 | else if (rParam.meVerJust==SvxCellVerJustify::Center) |
4192 | 0 | { |
4193 | 0 | if (rParam.mbPixelToLogic) |
4194 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, nTopM + ( |
4195 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
4196 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() ) |
4197 | 0 | / 2)).Height() ); |
4198 | 0 | else |
4199 | 0 | aLogicStart.AdjustY(nTopM + (aCellSize.Height() - nEngineHeight) / 2 ); |
4200 | 0 | } |
4201 | 0 | else // top |
4202 | 0 | { |
4203 | 0 | if (rParam.mbPixelToLogic) |
4204 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() ); |
4205 | 0 | else |
4206 | 0 | aLogicStart.AdjustY(nTopM ); |
4207 | 0 | } |
4208 | |
|
4209 | 0 | aURLStart = aLogicStart; // copy before modifying for orientation |
4210 | |
|
4211 | 0 | Size aPaperLogic = rParam.mpEngine->GetPaperSize(); |
4212 | 0 | aPaperLogic.setWidth( nEngineWidth ); |
4213 | 0 | rParam.mpEngine->SetPaperSize(aPaperLogic); |
4214 | | |
4215 | | // bMoveClipped handling has been replaced by complete alignment |
4216 | | // handling (also extending to the left). |
4217 | |
|
4218 | 0 | if (bSimClip) |
4219 | 0 | { |
4220 | | // no hard clip, only draw the affected rows |
4221 | 0 | Point aDocStart = aClip.getRect().TopLeft(); |
4222 | 0 | aDocStart -= aLogicStart; |
4223 | 0 | rParam.mpEngine->DrawText_ToRectangle(*mpDev, aClip.getRect(), aDocStart, false); |
4224 | 0 | } |
4225 | 0 | else |
4226 | 0 | { |
4227 | 0 | rParam.mpEngine->DrawText_ToPosition(*mpDev, aLogicStart); |
4228 | 0 | } |
4229 | 0 | } |
4230 | |
|
4231 | 0 | rParam.adjustForHyperlinkInPDF(aURLStart, mpDev); |
4232 | 0 | } |
4233 | | |
4234 | | void ScOutputData::DrawEditAsianVertical(DrawEditParam& rParam) |
4235 | 0 | { |
4236 | | // When in asian vertical orientation, the orientation value is STANDARD, |
4237 | | // and the asian vertical boolean is true. |
4238 | 0 | OSL_ASSERT(rParam.meOrient == SvxCellOrientation::Standard); |
4239 | 0 | OSL_ASSERT(rParam.mbAsianVertical); |
4240 | 0 | OSL_ASSERT(rParam.meHorJustAttr != SvxCellHorJustify::Repeat); |
4241 | |
|
4242 | 0 | Size aRefOne = mpRefDevice->PixelToLogic(Size(1,1)); |
4243 | |
|
4244 | 0 | bool bHidden = false; |
4245 | 0 | bool bShrink = !rParam.mbBreak && lcl_GetBoolValue(*rParam.mpPattern, ATTR_SHRINKTOFIT, rParam.mpCondSet); |
4246 | 0 | Degree100 nAttrRotate = lcl_GetValue<ScRotateValueItem, Degree100>(*rParam.mpPattern, ATTR_ROTATE_VALUE, rParam.mpCondSet); |
4247 | |
|
4248 | 0 | if (nAttrRotate) |
4249 | 0 | { |
4250 | | //! set flag to find the cell in DrawRotated again ? |
4251 | | //! (or flag already set during DrawBackground, then no query here) |
4252 | 0 | bHidden = true; // rotated is outputted separately |
4253 | 0 | } |
4254 | | |
4255 | | // default alignment for asian vertical mode is top-right |
4256 | | /* TODO: is setting meHorJustContext and meHorJustResult unconditionally to |
4257 | | * SvxCellHorJustify::Right really wanted? Seems this was done all the time, |
4258 | | * also before context was introduced and everything was attr only. */ |
4259 | 0 | if ( rParam.meHorJustAttr == SvxCellHorJustify::Standard ) |
4260 | 0 | rParam.meHorJustResult = rParam.meHorJustContext = SvxCellHorJustify::Right; |
4261 | |
|
4262 | 0 | if (bHidden) |
4263 | 0 | return; |
4264 | | |
4265 | 0 | SvxCellHorJustify eOutHorJust = rParam.meHorJustContext; |
4266 | | |
4267 | | //! mirror margin values for RTL? |
4268 | | //! move margin down to after final GetOutputArea call |
4269 | 0 | tools::Long nTopM, nLeftM, nBottomM, nRightM; |
4270 | 0 | rParam.calcMargins(nTopM, nLeftM, nBottomM, nRightM, mnPPTX, mnPPTY); |
4271 | |
|
4272 | 0 | SCCOL nXForPos = rParam.mnX; |
4273 | 0 | if ( nXForPos < mnX1 ) |
4274 | 0 | { |
4275 | 0 | nXForPos = mnX1; |
4276 | 0 | rParam.mnPosX = rParam.mnInitPosX; |
4277 | 0 | } |
4278 | 0 | SCSIZE nArrYForPos = rParam.mnArrY; |
4279 | 0 | if ( nArrYForPos < 1 ) |
4280 | 0 | { |
4281 | 0 | nArrYForPos = 1; |
4282 | 0 | rParam.mnPosY = mnScrY; |
4283 | 0 | } |
4284 | |
|
4285 | 0 | OutputAreaParam aAreaParam; |
4286 | | |
4287 | | // Initial page size - large for normal text, cell size for automatic line breaks |
4288 | |
|
4289 | 0 | Size aPaperSize( 1000000, 1000000 ); |
4290 | | // call GetOutputArea with nNeeded=0, to get only the cell width |
4291 | | |
4292 | | //! handle nArrY == 0 |
4293 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, 0, |
4294 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
4295 | 0 | rParam.mbCellIsValue, true, false, aAreaParam ); |
4296 | | |
4297 | | //! special ScEditUtil handling if formatting for printer |
4298 | 0 | rParam.calcPaperSize(aPaperSize, aAreaParam.maAlignRect, mnPPTX, mnPPTY); |
4299 | |
|
4300 | 0 | if (rParam.mbPixelToLogic) |
4301 | 0 | { |
4302 | 0 | Size aLogicSize = mpRefDevice->PixelToLogic(aPaperSize); |
4303 | 0 | if ( rParam.mbBreak && !rParam.mbAsianVertical && mpRefDevice != pFmtDevice ) |
4304 | 0 | { |
4305 | | // #i85342# screen display and formatting for printer, |
4306 | | // use same GetEditArea call as in ScViewData::SetEditEngine |
4307 | |
|
4308 | 0 | double fFract(1.0); |
4309 | 0 | tools::Rectangle aUtilRect = ScEditUtil( *mpDoc, rParam.mnCellX, rParam.mnCellY, mnTab, Point(0,0), pFmtDevice, |
4310 | 0 | HMM_PER_TWIPS, HMM_PER_TWIPS, fFract, fFract ).GetEditArea( rParam.mpPattern, false ); |
4311 | 0 | aLogicSize.setWidth( aUtilRect.GetWidth() ); |
4312 | 0 | } |
4313 | 0 | rParam.mpEngine->SetPaperSize(aLogicSize); |
4314 | 0 | } |
4315 | 0 | else |
4316 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
4317 | | |
4318 | | // Fill the EditEngine (cell attributes and text) |
4319 | | |
4320 | | // default alignment for asian vertical mode is top-right |
4321 | 0 | if ( rParam.meVerJust == SvxCellVerJustify::Standard ) |
4322 | 0 | rParam.meVerJust = SvxCellVerJustify::Top; |
4323 | |
|
4324 | 0 | rParam.setPatternToEngine(mbUseStyleColor); |
4325 | 0 | rParam.setAlignmentToEngine(); |
4326 | | |
4327 | | // Read content from cell |
4328 | |
|
4329 | 0 | bool bWrapFields = false; |
4330 | 0 | if (!rParam.readCellContent(mpDoc, mbShowNullValues, mbShowFormulas, mbSyntaxMode, mbUseStyleColor, mbForceAutoColor, bWrapFields)) |
4331 | | // Failed to read cell content. Bail out. |
4332 | 0 | return; |
4333 | | |
4334 | 0 | if ( mbSyntaxMode ) |
4335 | 0 | SetEditSyntaxColor( *rParam.mpEngine, rParam.maCell ); |
4336 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
4337 | 0 | lcl_SetEditColor( *rParam.mpEngine, COL_AUTO ); //! or have a flag at EditEngine |
4338 | |
|
4339 | 0 | rParam.mpEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
4340 | | |
4341 | | // Get final output area using the calculated width |
4342 | |
|
4343 | 0 | tools::Long nEngineWidth, nEngineHeight; |
4344 | 0 | rParam.getEngineSize(rParam.mpEngine, nEngineWidth, nEngineHeight); |
4345 | |
|
4346 | 0 | tools::Long nNeededPixel = nEngineWidth; |
4347 | 0 | if (rParam.mbPixelToLogic) |
4348 | 0 | nNeededPixel = mpRefDevice->LogicToPixel(Size(nNeededPixel,0)).Width(); |
4349 | 0 | nNeededPixel += nLeftM + nRightM; |
4350 | | |
4351 | | // for break, the first GetOutputArea call is sufficient |
4352 | 0 | GetOutputArea( nXForPos, nArrYForPos, rParam.mnPosX, rParam.mnPosY, rParam.mnCellX, rParam.mnCellY, nNeededPixel, |
4353 | 0 | *rParam.mpPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
4354 | 0 | rParam.mbCellIsValue || bShrink, false, false, aAreaParam ); |
4355 | |
|
4356 | 0 | if ( bShrink ) |
4357 | 0 | { |
4358 | 0 | ShrinkEditEngine( *rParam.mpEngine, aAreaParam.maAlignRect, |
4359 | 0 | nLeftM, nTopM, nRightM, nBottomM, false, |
4360 | 0 | rParam.meOrient, 0_deg100, rParam.mbPixelToLogic, |
4361 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, |
4362 | 0 | aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
4363 | 0 | } |
4364 | 0 | if ( rParam.mbCellIsValue && ( aAreaParam.mbLeftClip || aAreaParam.mbRightClip ) ) |
4365 | 0 | { |
4366 | 0 | nEngineWidth = SetEngineTextAndGetWidth( rParam, u"###"_ustr, nNeededPixel, ( nLeftM + nRightM ) ); |
4367 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
4368 | 0 | ScCellInfo* pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
4369 | 0 | SetClipMarks( aAreaParam, pClipMarkCell, eOutHorJust, nLayoutSign ); |
4370 | 0 | } |
4371 | |
|
4372 | 0 | if (eOutHorJust != SvxCellHorJustify::Left) |
4373 | 0 | { |
4374 | 0 | aPaperSize.setWidth( nNeededPixel + 1 ); |
4375 | 0 | if (rParam.mbPixelToLogic) |
4376 | 0 | rParam.mpEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
4377 | 0 | else |
4378 | 0 | rParam.mpEngine->SetPaperSize(aPaperSize); |
4379 | 0 | } |
4380 | |
|
4381 | 0 | tools::Long nStartX = aAreaParam.maAlignRect.Left(); |
4382 | 0 | tools::Long nStartY = aAreaParam.maAlignRect.Top(); |
4383 | 0 | tools::Long nCellWidth = aAreaParam.maAlignRect.GetWidth(); |
4384 | 0 | tools::Long nOutWidth = nCellWidth - 1 - nLeftM - nRightM; |
4385 | 0 | tools::Long nOutHeight = aAreaParam.maAlignRect.GetHeight() - nTopM - nBottomM; |
4386 | | |
4387 | | // text with automatic breaks is aligned only within the |
4388 | | // edit engine's paper size, the output of the whole area |
4389 | | // is always left-aligned |
4390 | |
|
4391 | 0 | nStartX += nLeftM; |
4392 | |
|
4393 | 0 | bool bOutside = (aAreaParam.maClipRect.Right() < mnScrX || aAreaParam.maClipRect.Left() >= mnScrX + mnScrW); |
4394 | 0 | if (bOutside) |
4395 | 0 | return; |
4396 | | |
4397 | | // Also take fields in a cell with automatic breaks into account: clip to cell width |
4398 | 0 | bool bClip = AdjustAreaParamClipRect(aAreaParam) || aAreaParam.mbLeftClip || aAreaParam.mbRightClip || bWrapFields; |
4399 | 0 | bool bSimClip = false; |
4400 | |
|
4401 | 0 | Size aCellSize; // output area, excluding margins, in logical units |
4402 | 0 | if (rParam.mbPixelToLogic) |
4403 | 0 | aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); |
4404 | 0 | else |
4405 | 0 | aCellSize = Size( nOutWidth, nOutHeight ); |
4406 | |
|
4407 | 0 | if ( nEngineHeight >= aCellSize.Height() + aRefOne.Height() ) |
4408 | 0 | { |
4409 | 0 | const ScMergeAttr* pMerge = &rParam.mpPattern->GetItem(ATTR_MERGE); |
4410 | 0 | bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; |
4411 | | |
4412 | | // Don't clip for text height when printing rows with optimal height, |
4413 | | // except when font size is from conditional formatting. |
4414 | | //! Allow clipping when vertically merged? |
4415 | 0 | if ( meType != OUTTYPE_PRINTER || |
4416 | 0 | ( mpDoc->GetRowFlags( rParam.mnCellY, mnTab ) & CRFlags::ManualSize ) || |
4417 | 0 | ( rParam.mpCondSet && SfxItemState::SET == |
4418 | 0 | rParam.mpCondSet->GetItemState(ATTR_FONT_HEIGHT) ) ) |
4419 | 0 | bClip = true; |
4420 | 0 | else |
4421 | 0 | bSimClip = true; |
4422 | | |
4423 | | // Show clip marks if height is at least 5pt too small and |
4424 | | // there are several lines of text. |
4425 | | // Not for asian vertical text, because that would interfere |
4426 | | // with the default right position of the text. |
4427 | | // Only with automatic line breaks, to avoid having to find |
4428 | | // the cells with the horizontal end of the text again. |
4429 | 0 | if ( nEngineHeight - aCellSize.Height() > 100 && |
4430 | 0 | ( rParam.mbBreak || rParam.meOrient == SvxCellOrientation::Stacked ) && |
4431 | 0 | !rParam.mbAsianVertical && mbMarkClipped && |
4432 | 0 | ( rParam.mpEngine->GetParagraphCount() > 1 || rParam.mpEngine->GetLineCount(0) > 1 ) ) |
4433 | 0 | { |
4434 | 0 | ScCellInfo* pClipMarkCell = nullptr; |
4435 | 0 | if ( bMerged ) |
4436 | 0 | { |
4437 | | // anywhere in the merged area... |
4438 | 0 | SCCOL nClipX = ( rParam.mnX < mnX1 ) ? mnX1 : rParam.mnX; |
4439 | 0 | pClipMarkCell = &mpRowInfo[(rParam.mnArrY != 0) ? rParam.mnArrY : 1].cellInfo(nClipX); |
4440 | 0 | } |
4441 | 0 | else |
4442 | 0 | pClipMarkCell = &rParam.mpThisRowInfo->cellInfo(rParam.mnX); |
4443 | |
|
4444 | 0 | pClipMarkCell->nClipMark |= ScClipMark::Right; //! also allow left? |
4445 | 0 | mbAnyClipped = true; |
4446 | |
|
4447 | 0 | tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX ); |
4448 | 0 | if ( aAreaParam.maClipRect.Right() - nMarkPixel > aAreaParam.maClipRect.Left() ) |
4449 | 0 | aAreaParam.maClipRect.AdjustRight( -nMarkPixel ); |
4450 | 0 | } |
4451 | 0 | } |
4452 | |
|
4453 | 0 | Point aURLStart; |
4454 | |
|
4455 | 0 | { // Clip marks are already handled in GetOutputArea |
4456 | 0 | ClearableClipRegion aClip(rParam.mbPixelToLogic ? mpRefDevice->PixelToLogic(aAreaParam.maClipRect) |
4457 | 0 | : aAreaParam.maClipRect, bClip, bSimClip, mpDev, mbMetaFile); |
4458 | |
|
4459 | 0 | Point aLogicStart; |
4460 | 0 | if (rParam.mbPixelToLogic) |
4461 | 0 | aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) ); |
4462 | 0 | else |
4463 | 0 | aLogicStart = Point(nStartX, nStartY); |
4464 | |
|
4465 | 0 | tools::Long nAvailWidth = aCellSize.Width(); |
4466 | | // space for AutoFilter is already handled in GetOutputArea |
4467 | | |
4468 | | // horizontal alignment |
4469 | |
|
4470 | 0 | if (rParam.meHorJustResult==SvxCellHorJustify::Right) |
4471 | 0 | aLogicStart.AdjustX(nAvailWidth - nEngineWidth ); |
4472 | 0 | else if (rParam.meHorJustResult==SvxCellHorJustify::Center) |
4473 | 0 | aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 ); |
4474 | | |
4475 | | // paper size is subtracted below |
4476 | 0 | aLogicStart.AdjustX(nEngineWidth ); |
4477 | | |
4478 | | // vertical adjustment is within the EditEngine |
4479 | 0 | if (rParam.mbPixelToLogic) |
4480 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic(Size(0,nTopM)).Height() ); |
4481 | 0 | else |
4482 | 0 | aLogicStart.AdjustY(nTopM ); |
4483 | |
|
4484 | 0 | aURLStart = aLogicStart; // copy before modifying for orientation |
4485 | | |
4486 | | // bMoveClipped handling has been replaced by complete alignment |
4487 | | // handling (also extending to the left). |
4488 | | |
4489 | | // with SetVertical, the start position is top left of |
4490 | | // the whole output area, not the text itself |
4491 | 0 | aLogicStart.AdjustX( -(rParam.mpEngine->GetPaperSize().Width()) ); |
4492 | |
|
4493 | 0 | rParam.mpEngine->DrawText_ToPosition(*mpDev, aLogicStart); |
4494 | 0 | } |
4495 | |
|
4496 | 0 | rParam.adjustForHyperlinkInPDF(aURLStart, mpDev); |
4497 | 0 | } |
4498 | | |
4499 | | void ScOutputData::DrawEdit(bool bPixelToLogic) |
4500 | 0 | { |
4501 | 0 | vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData()); |
4502 | 0 | bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF(); |
4503 | |
|
4504 | 0 | InitOutputEditEngine(); |
4505 | |
|
4506 | 0 | bool bHyphenatorSet = false; |
4507 | 0 | const ScPatternAttr* pOldPattern = nullptr; |
4508 | 0 | const SfxItemSet* pOldCondSet = nullptr; |
4509 | 0 | const SfxItemSet* pOldTableSet = nullptr; |
4510 | 0 | const SfxItemSet* pOldPreviewFontSet = nullptr; |
4511 | 0 | ScRefCellValue aCell; |
4512 | |
|
4513 | 0 | tools::Long nInitPosX = mnScrX; |
4514 | 0 | if ( mbLayoutRTL ) |
4515 | 0 | { |
4516 | 0 | nInitPosX += mnMirrorW - 1; |
4517 | 0 | } |
4518 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
4519 | |
|
4520 | 0 | SCCOL nLastContentCol = mpDoc->MaxCol(); |
4521 | 0 | if ( mnX2 < mpDoc->MaxCol() ) |
4522 | 0 | { |
4523 | 0 | SCROW nEndRow; |
4524 | 0 | mpDoc->GetCellArea(mnTab, nLastContentCol, nEndRow); |
4525 | 0 | } |
4526 | |
|
4527 | 0 | tools::Long nRowPosY = mnScrY; |
4528 | 0 | for (SCSIZE nArrY=0; nArrY+1<mnArrCount; nArrY++) // 0 of the rest of the merged |
4529 | 0 | { |
4530 | 0 | RowInfo* pThisRowInfo = &mpRowInfo[nArrY]; |
4531 | |
|
4532 | 0 | if (nArrY==1) nRowPosY = mnScrY; // positions before are calculated individually |
4533 | |
|
4534 | 0 | if ( pThisRowInfo->bChanged || nArrY==0 ) |
4535 | 0 | { |
4536 | 0 | tools::Long nPosX = 0; |
4537 | 0 | for (SCCOL nX=0; nX<=mnX2; nX++) // due to overflow |
4538 | 0 | { |
4539 | 0 | std::unique_ptr< ScPatternAttr > pPreviewPattr; |
4540 | 0 | if (nX==mnX1) nPosX = nInitPosX; // positions before mnX1 are calculated individually |
4541 | |
|
4542 | 0 | if (pThisRowInfo->basicCellInfo(nX).bEditEngine) |
4543 | 0 | { |
4544 | 0 | SCROW nY = pThisRowInfo->nRowNo; |
4545 | |
|
4546 | 0 | bool bReopenTag = false; |
4547 | 0 | if (bTaggedPDF) |
4548 | 0 | bReopenTag = ReopenPDFStructureElement(vcl::pdf::StructElement::TableData, nY, nX); |
4549 | |
|
4550 | 0 | SCCOL nCellX = nX; // position where the cell really starts |
4551 | 0 | SCROW nCellY = nY; |
4552 | 0 | bool bDoCell = false; |
4553 | | |
4554 | | // if merged cell contains hidden row or column or both |
4555 | 0 | const ScMergeFlagAttr& rMergeFlag = mpDoc->GetAttr(nX, nY, mnTab, ATTR_MERGE_FLAG); |
4556 | 0 | bool bOverlapped = (rMergeFlag.IsHorOverlapped() || rMergeFlag.IsVerOverlapped()); |
4557 | |
|
4558 | 0 | tools::Long nPosY = nRowPosY; |
4559 | 0 | if (bOverlapped) |
4560 | 0 | { |
4561 | 0 | nY = mpRowInfo[nArrY].nRowNo; |
4562 | 0 | SCCOL nOverX; // start of the merged cells |
4563 | 0 | SCROW nOverY; |
4564 | 0 | if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, true )) |
4565 | 0 | { |
4566 | 0 | nCellX = nOverX; |
4567 | 0 | nCellY = nOverY; |
4568 | 0 | bDoCell = true; |
4569 | 0 | } |
4570 | 0 | } |
4571 | 0 | else if ( nX == mnX2 && pThisRowInfo->cellInfo(nX).maCell.isEmpty() ) |
4572 | 0 | { |
4573 | | // Rest of a long text further to the right? |
4574 | |
|
4575 | 0 | SCCOL nTempX=nX; |
4576 | 0 | while (nTempX < nLastContentCol && IsEmptyCellText( pThisRowInfo, nTempX, nY )) |
4577 | 0 | ++nTempX; |
4578 | |
|
4579 | 0 | if ( nTempX > nX && |
4580 | 0 | !IsEmptyCellText( pThisRowInfo, nTempX, nY ) && |
4581 | 0 | !mpDoc->HasAttrib( nTempX,nY,mnTab, nX,nY,mnTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) |
4582 | 0 | { |
4583 | 0 | nCellX = nTempX; |
4584 | 0 | bDoCell = true; |
4585 | 0 | } |
4586 | 0 | } |
4587 | 0 | else |
4588 | 0 | { |
4589 | 0 | bDoCell = true; |
4590 | 0 | } |
4591 | |
|
4592 | 0 | if ( bDoCell && mbEditMode && nCellX == mnEditCol && nCellY == mnEditRow ) |
4593 | 0 | bDoCell = false; |
4594 | |
|
4595 | 0 | const ScPatternAttr* pPattern = nullptr; |
4596 | 0 | const SfxItemSet* pCondSet = nullptr; |
4597 | 0 | const SfxItemSet* pTableSet = nullptr; |
4598 | 0 | if (bDoCell) |
4599 | 0 | { |
4600 | 0 | if ( nCellY == nY && nCellX >= mnX1 && nCellX <= mnX2 && |
4601 | 0 | !mpDoc->ColHidden(nCellX, mnTab) ) |
4602 | 0 | { |
4603 | 0 | ScCellInfo& rCellInfo = pThisRowInfo->cellInfo(nCellX); |
4604 | 0 | pPattern = rCellInfo.pPatternAttr; |
4605 | 0 | pCondSet = rCellInfo.pConditionSet; |
4606 | 0 | pTableSet = rCellInfo.pTableFormatSet; |
4607 | 0 | aCell = rCellInfo.maCell; |
4608 | 0 | } |
4609 | 0 | else // get from document |
4610 | 0 | { |
4611 | 0 | pPattern = mpDoc->GetPattern( nCellX, nCellY, mnTab ); |
4612 | 0 | pCondSet = mpDoc->GetCondResult( nCellX, nCellY, mnTab ); |
4613 | 0 | pTableSet = mpDoc->GetTableFormatSet( nCellX, nCellY, mnTab ); |
4614 | 0 | GetVisibleCell( nCellX, nCellY, mnTab, aCell ); |
4615 | 0 | } |
4616 | 0 | if (aCell.isEmpty()) |
4617 | 0 | bDoCell = false; |
4618 | 0 | } |
4619 | 0 | if (bDoCell) |
4620 | 0 | { |
4621 | 0 | if ( mpDoc->GetPreviewCellStyle() ) |
4622 | 0 | { |
4623 | 0 | if ( ScStyleSheet* pPreviewStyle = mpDoc->GetPreviewCellStyle( nCellX, nCellY, mnTab ) ) |
4624 | 0 | { |
4625 | 0 | pPreviewPattr.reset( new ScPatternAttr(*pPattern) ); |
4626 | 0 | pPreviewPattr->SetStyleSheet(pPreviewStyle); |
4627 | 0 | pPattern = pPreviewPattr.get(); |
4628 | 0 | } |
4629 | 0 | } |
4630 | 0 | SfxItemSet* pPreviewFontSet = mpDoc->GetPreviewFont( nCellX, nCellY, mnTab ); |
4631 | 0 | lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False) |
4632 | | |
4633 | | // fdo#32530: Check if the first character is RTL. |
4634 | 0 | OUString aStr = mpDoc->GetString(nCellX, nCellY, mnTab); |
4635 | |
|
4636 | 0 | DrawEditParam aParam(pPattern, pCondSet, pTableSet, lcl_SafeIsValue(aCell)); |
4637 | 0 | const bool bNumberFormatIsText = lcl_isNumberFormatText( mpDoc, nCellX, nCellY, mnTab ); |
4638 | 0 | aParam.meHorJustContext = getAlignmentFromContext( aParam.meHorJustAttr, |
4639 | 0 | aParam.mbCellIsValue, aStr, *pPattern, pCondSet, mpDoc, mnTab, bNumberFormatIsText); |
4640 | 0 | aParam.meHorJustResult = (aParam.meHorJustAttr == SvxCellHorJustify::Block) ? |
4641 | 0 | SvxCellHorJustify::Block : aParam.meHorJustContext; |
4642 | 0 | aParam.mbPixelToLogic = bPixelToLogic; |
4643 | 0 | aParam.mbHyphenatorSet = bHyphenatorSet; |
4644 | 0 | aParam.mpEngine = mxOutputEditEngine.get(); |
4645 | 0 | aParam.maCell = aCell; |
4646 | 0 | aParam.mnArrY = nArrY; |
4647 | 0 | aParam.mnX = nX; |
4648 | 0 | aParam.mnCellX = nCellX; |
4649 | 0 | aParam.mnCellY = nCellY; |
4650 | 0 | aParam.mnPosX = nPosX; |
4651 | 0 | aParam.mnPosY = nPosY; |
4652 | 0 | aParam.mnInitPosX = nInitPosX; |
4653 | 0 | aParam.mpPreviewFontSet = pPreviewFontSet; |
4654 | 0 | aParam.mpOldPattern = pOldPattern; |
4655 | 0 | aParam.mpOldCondSet = pOldCondSet; |
4656 | 0 | aParam.mpOldTableSet = pOldTableSet; |
4657 | 0 | aParam.mpOldPreviewFontSet = pOldPreviewFontSet; |
4658 | 0 | aParam.mpThisRowInfo = pThisRowInfo; |
4659 | 0 | if (mpSpellCheckCxt) |
4660 | 0 | aParam.maMisspellRanges = mpSpellCheckCxt->getMisspellRanges(nCellX, nCellY); |
4661 | |
|
4662 | 0 | if (aParam.meHorJustAttr == SvxCellHorJustify::Repeat) |
4663 | 0 | { |
4664 | | // ignore orientation/rotation if "repeat" is active |
4665 | 0 | aParam.meOrient = SvxCellOrientation::Standard; |
4666 | 0 | } |
4667 | 0 | switch (aParam.meOrient) |
4668 | 0 | { |
4669 | 0 | case SvxCellOrientation::BottomUp: |
4670 | 0 | DrawEditBottomTop(aParam); |
4671 | 0 | break; |
4672 | 0 | case SvxCellOrientation::TopBottom: |
4673 | 0 | DrawEditTopBottom(aParam); |
4674 | 0 | break; |
4675 | 0 | case SvxCellOrientation::Stacked: |
4676 | | // this can be vertically stacked or asian vertical. |
4677 | 0 | DrawEditStacked(aParam); |
4678 | 0 | break; |
4679 | 0 | default: |
4680 | 0 | DrawEditStandard(aParam); |
4681 | 0 | } |
4682 | | |
4683 | | // Retrieve parameters for next iteration. |
4684 | 0 | pOldPattern = aParam.mpOldPattern; |
4685 | 0 | pOldCondSet = aParam.mpOldCondSet; |
4686 | 0 | pOldTableSet = aParam.mpOldTableSet; |
4687 | 0 | pOldPreviewFontSet = aParam.mpOldPreviewFontSet; |
4688 | 0 | bHyphenatorSet = aParam.mbHyphenatorSet; |
4689 | 0 | } |
4690 | 0 | if (bReopenTag) |
4691 | 0 | pPDF->EndStructureElement(); |
4692 | 0 | } |
4693 | 0 | nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign; |
4694 | 0 | } |
4695 | 0 | } |
4696 | 0 | nRowPosY += mpRowInfo[nArrY].nHeight; |
4697 | 0 | } |
4698 | | |
4699 | 0 | if (mrTabInfo.maArray.HasCellRotation()) |
4700 | 0 | { |
4701 | 0 | DrawRotated(bPixelToLogic); //! call from outside ? |
4702 | 0 | } |
4703 | 0 | } |
4704 | | |
4705 | | void ScOutputData::DrawRotated(bool bPixelToLogic) |
4706 | 0 | { |
4707 | 0 | InitOutputEditEngine(); |
4708 | | //! store nRotMax |
4709 | 0 | SCCOL nRotMax = mnX2; |
4710 | 0 | for (SCSIZE nRotY=0; nRotY<mnArrCount; nRotY++) |
4711 | 0 | if (mpRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && mpRowInfo[nRotY].nRotMaxCol > nRotMax) |
4712 | 0 | nRotMax = mpRowInfo[nRotY].nRotMaxCol; |
4713 | |
|
4714 | 0 | Color nConfBackColor = GetConfBackgroundColor(); |
4715 | 0 | bool bCellContrast = mbUseStyleColor && |
4716 | 0 | Application::GetSettings().GetStyleSettings().GetHighContrastMode(); |
4717 | |
|
4718 | 0 | bool bHyphenatorSet = false; |
4719 | 0 | const ScPatternAttr* pPattern; |
4720 | 0 | const SfxItemSet* pCondSet; |
4721 | 0 | const ScPatternAttr* pOldPattern = nullptr; |
4722 | 0 | const SfxItemSet* pOldCondSet = nullptr; |
4723 | 0 | ScRefCellValue aCell; |
4724 | |
|
4725 | 0 | tools::Long nInitPosX = mnScrX; |
4726 | 0 | if ( mbLayoutRTL ) |
4727 | 0 | { |
4728 | 0 | nInitPosX += mnMirrorW - 1; |
4729 | 0 | } |
4730 | 0 | tools::Long nLayoutSign = mbLayoutRTL ? -1 : 1; |
4731 | |
|
4732 | 0 | tools::Long nRowPosY = mnScrY; |
4733 | 0 | for (SCSIZE nArrY=0; nArrY+1<mnArrCount; nArrY++) // 0 for the rest of the merged |
4734 | 0 | { |
4735 | 0 | RowInfo* pThisRowInfo = &mpRowInfo[nArrY]; |
4736 | 0 | tools::Long nCellHeight = static_cast<tools::Long>(pThisRowInfo->nHeight); |
4737 | 0 | if (nArrY==1) nRowPosY = mnScrY; // positions before are calculated individually |
4738 | |
|
4739 | 0 | if ( ( pThisRowInfo->bChanged || nArrY==0 ) && pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE ) |
4740 | 0 | { |
4741 | 0 | tools::Long nPosX = 0; |
4742 | 0 | for (SCCOL nX=0; nX<=nRotMax; nX++) |
4743 | 0 | { |
4744 | 0 | if (nX==mnX1) nPosX = nInitPosX; // positions before mnX1 are calculated individually |
4745 | |
|
4746 | 0 | const ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX); |
4747 | 0 | if ( pInfo->nRotateDir != ScRotateDir::NONE ) |
4748 | 0 | { |
4749 | 0 | SCROW nY = pThisRowInfo->nRowNo; |
4750 | |
|
4751 | 0 | bool bHidden = false; |
4752 | 0 | if (mbEditMode) |
4753 | 0 | if ( nX == mnEditCol && nY == mnEditRow ) |
4754 | 0 | bHidden = true; |
4755 | |
|
4756 | 0 | if (!bHidden) |
4757 | 0 | { |
4758 | 0 | lcl_ClearEdit( *mxOutputEditEngine ); // also calls SetUpdateMode(sal_False) |
4759 | |
|
4760 | 0 | tools::Long nPosY = nRowPosY; |
4761 | | |
4762 | | //! rest from merged cells further up do not work! |
4763 | |
|
4764 | 0 | bool bFromDoc = false; |
4765 | 0 | pPattern = pInfo->pPatternAttr; |
4766 | 0 | pCondSet = pInfo->pConditionSet; |
4767 | 0 | if (!pPattern) |
4768 | 0 | { |
4769 | 0 | pPattern = mpDoc->GetPattern( nX, nY, mnTab ); |
4770 | 0 | bFromDoc = true; |
4771 | 0 | } |
4772 | 0 | aCell = pInfo->maCell; |
4773 | 0 | if (bFromDoc) |
4774 | 0 | pCondSet = mpDoc->GetCondResult( nX, nY, mnTab ); |
4775 | |
|
4776 | 0 | if (aCell.isEmpty() && nX>mnX2) |
4777 | 0 | GetVisibleCell( nX, nY, mnTab, aCell ); |
4778 | |
|
4779 | 0 | if (aCell.isEmpty() || IsEmptyCellText(pThisRowInfo, nX, nY)) |
4780 | 0 | bHidden = true; // nRotateDir is also set without a cell |
4781 | |
|
4782 | 0 | tools::Long nCellWidth = static_cast<tools::Long>(mpRowInfo[0].basicCellInfo(nX).nWidth); |
4783 | |
|
4784 | 0 | SvxCellHorJustify eHorJust = |
4785 | 0 | pPattern->GetItem(ATTR_HOR_JUSTIFY, pCondSet).GetValue(); |
4786 | 0 | bool bBreak = ( eHorJust == SvxCellHorJustify::Block ) || |
4787 | 0 | pPattern->GetItem(ATTR_LINEBREAK, pCondSet).GetValue(); |
4788 | 0 | bool bRepeat = ( eHorJust == SvxCellHorJustify::Repeat && !bBreak ); |
4789 | 0 | bool bShrink = !bBreak && !bRepeat && |
4790 | 0 | pPattern->GetItem( ATTR_SHRINKTOFIT, pCondSet ).GetValue(); |
4791 | 0 | SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); |
4792 | |
|
4793 | 0 | const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE); |
4794 | 0 | bool bMerged = pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1; |
4795 | |
|
4796 | 0 | tools::Long nStartX = nPosX; |
4797 | 0 | tools::Long nStartY = nPosY; |
4798 | 0 | if (nX<mnX1) |
4799 | 0 | { |
4800 | 0 | if ((bBreak || eOrient!=SvxCellOrientation::Standard) && !bMerged) |
4801 | 0 | bHidden = true; |
4802 | 0 | else |
4803 | 0 | { |
4804 | 0 | nStartX = nInitPosX; |
4805 | 0 | SCCOL nCol = mnX1; |
4806 | 0 | while (nCol > nX) |
4807 | 0 | { |
4808 | 0 | --nCol; |
4809 | 0 | nStartX -= nLayoutSign * static_cast<tools::Long>(mpRowInfo[0].basicCellInfo(nCol).nWidth); |
4810 | 0 | } |
4811 | 0 | } |
4812 | 0 | } |
4813 | 0 | tools::Long nCellStartX = nStartX; |
4814 | | |
4815 | | // omit substitute representation of small text |
4816 | |
|
4817 | 0 | if (!bHidden) |
4818 | 0 | { |
4819 | 0 | tools::Long nOutWidth = nCellWidth - 1; |
4820 | 0 | tools::Long nOutHeight = nCellHeight; |
4821 | |
|
4822 | 0 | if ( bMerged ) |
4823 | 0 | { |
4824 | 0 | SCCOL nCountX = pMerge->GetColMerge(); |
4825 | 0 | for (SCCOL i=1; i<nCountX; i++) |
4826 | 0 | nOutWidth += mpDoc->GetColWidth(nX+i,mnTab) * mnPPTX; |
4827 | 0 | SCROW nCountY = pMerge->GetRowMerge(); |
4828 | 0 | nOutHeight += mpDoc->GetScaledRowHeight( nY+1, nY+nCountY-1, mnTab, mnPPTY); |
4829 | 0 | } |
4830 | |
|
4831 | 0 | SvxCellVerJustify eVerJust = |
4832 | 0 | pPattern->GetItem(ATTR_VER_JUSTIFY, pCondSet).GetValue(); |
4833 | | |
4834 | | // syntax mode is ignored here... |
4835 | | |
4836 | | // StringDiffer doesn't look at hyphenate, language items |
4837 | 0 | if ( !ScPatternAttr::areSame(pPattern, pOldPattern) || pCondSet != pOldCondSet ) |
4838 | 0 | { |
4839 | 0 | SfxItemSet aSet( mxOutputEditEngine->GetEmptyItemSet() ); |
4840 | 0 | pPattern->FillEditItemSet( &aSet, pCondSet ); |
4841 | | |
4842 | | // adjustment for EditEngine |
4843 | 0 | SvxAdjust eSvxAdjust = SvxAdjust::Left; |
4844 | 0 | if (eOrient==SvxCellOrientation::Stacked) |
4845 | 0 | eSvxAdjust = SvxAdjust::Center; |
4846 | | // adjustment for bBreak is omitted here |
4847 | 0 | aSet.Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); |
4848 | |
|
4849 | 0 | bool bParaHyphenate = aSet.Get(EE_PARA_HYPHENATE).GetValue(); |
4850 | 0 | mxOutputEditEngine->SetDefaults( std::move(aSet) ); |
4851 | 0 | pOldPattern = pPattern; |
4852 | 0 | pOldCondSet = pCondSet; |
4853 | |
|
4854 | 0 | EEControlBits nControl = mxOutputEditEngine->GetControlWord(); |
4855 | 0 | if (eOrient==SvxCellOrientation::Stacked) |
4856 | 0 | nControl |= EEControlBits::ONECHARPERLINE; |
4857 | 0 | else |
4858 | 0 | nControl &= ~EEControlBits::ONECHARPERLINE; |
4859 | 0 | mxOutputEditEngine->SetControlWord( nControl ); |
4860 | |
|
4861 | 0 | if ( !bHyphenatorSet && bParaHyphenate ) |
4862 | 0 | { |
4863 | | // set hyphenator the first time it is needed |
4864 | 0 | css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); |
4865 | 0 | mxOutputEditEngine->SetHyphenator( xXHyphenator ); |
4866 | 0 | bHyphenatorSet = true; |
4867 | 0 | } |
4868 | |
|
4869 | 0 | Color aBackCol = |
4870 | 0 | pPattern->GetItem( ATTR_BACKGROUND, pCondSet ).GetColor(); |
4871 | 0 | if ( mbUseStyleColor && ( aBackCol.IsTransparent() || bCellContrast ) ) |
4872 | 0 | aBackCol = nConfBackColor; |
4873 | 0 | mxOutputEditEngine->SetBackgroundColor( aBackCol ); |
4874 | 0 | } |
4875 | | |
4876 | | // margins |
4877 | | |
4878 | | //! change position and paper size to EditUtil !!! |
4879 | |
|
4880 | 0 | const SvxMarginItem* pMargin = |
4881 | 0 | &pPattern->GetItem(ATTR_MARGIN, pCondSet); |
4882 | 0 | sal_uInt16 nIndent = 0; |
4883 | 0 | if ( eHorJust == SvxCellHorJustify::Left ) |
4884 | 0 | nIndent = pPattern->GetItem(ATTR_INDENT, pCondSet).GetValue(); |
4885 | |
|
4886 | 0 | tools::Long nTotalHeight = nOutHeight; // without subtracting the margin |
4887 | 0 | if ( bPixelToLogic ) |
4888 | 0 | nTotalHeight = mpRefDevice->PixelToLogic(Size(0,nTotalHeight)).Height(); |
4889 | |
|
4890 | 0 | tools::Long nLeftM = static_cast<tools::Long>( (pMargin->GetLeftMargin() + nIndent) * mnPPTX ); |
4891 | 0 | tools::Long nTopM = static_cast<tools::Long>( pMargin->GetTopMargin() * mnPPTY ); |
4892 | 0 | tools::Long nRightM = static_cast<tools::Long>( pMargin->GetRightMargin() * mnPPTX ); |
4893 | 0 | tools::Long nBottomM = static_cast<tools::Long>( pMargin->GetBottomMargin() * mnPPTY ); |
4894 | 0 | nStartX += nLeftM; |
4895 | 0 | nStartY += nTopM; |
4896 | 0 | nOutWidth -= nLeftM + nRightM; |
4897 | 0 | nOutHeight -= nTopM + nBottomM; |
4898 | | |
4899 | | // rotate here already, to adjust paper size for page breaks |
4900 | 0 | Degree100 nAttrRotate; |
4901 | 0 | double nSin = 0.0; |
4902 | 0 | double nCos = 1.0; |
4903 | 0 | SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD; |
4904 | 0 | if ( eOrient == SvxCellOrientation::Standard ) |
4905 | 0 | { |
4906 | 0 | nAttrRotate = pPattern-> |
4907 | 0 | GetItem(ATTR_ROTATE_VALUE, pCondSet).GetValue(); |
4908 | 0 | if ( nAttrRotate ) |
4909 | 0 | { |
4910 | 0 | eRotMode = pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue(); |
4911 | | |
4912 | | // tdf#143377 To use the same limits to avoid too big Skew |
4913 | | // with TextOrientation in Calc, use 1/2 degree here, too. |
4914 | | // This equals '50' in the notation here (100th degree) |
4915 | 0 | static const sal_Int32 nMinRad(50); |
4916 | | |
4917 | | // bring nAttrRotate to the range [0..36000[ |
4918 | 0 | nAttrRotate = Degree100(((nAttrRotate.get() % 36000) + 36000) % 36000); |
4919 | | |
4920 | | // check for to be avoided extreme values and correct |
4921 | 0 | if (nAttrRotate < Degree100(nMinRad)) |
4922 | 0 | { |
4923 | | // range [0..50] |
4924 | 0 | nAttrRotate = Degree100(nMinRad); |
4925 | 0 | eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow |
4926 | 0 | } |
4927 | 0 | else if (nAttrRotate > Degree100(36000 - nMinRad)) |
4928 | 0 | { |
4929 | | // range [35950..36000[ |
4930 | 0 | nAttrRotate = Degree100(36000 - nMinRad); |
4931 | 0 | eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow |
4932 | 0 | } |
4933 | 0 | else if (nAttrRotate > Degree100(18000 - nMinRad) && (nAttrRotate < Degree100(18000 + nMinRad))) |
4934 | 0 | { |
4935 | | // range 50 around 18000, [17950..18050] |
4936 | 0 | nAttrRotate = (nAttrRotate > Degree100(18000)) |
4937 | 0 | ? Degree100(18000 + nMinRad) |
4938 | 0 | : Degree100(18000 - nMinRad); |
4939 | 0 | eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow |
4940 | 0 | } |
4941 | |
|
4942 | 0 | if ( mbLayoutRTL ) |
4943 | 0 | { |
4944 | | // keep in range [0..36000[ |
4945 | 0 | nAttrRotate = Degree100(36000 - nAttrRotate.get()); |
4946 | 0 | } |
4947 | |
|
4948 | 0 | double nRealOrient = toRadians(nAttrRotate); // 1/100 degree |
4949 | 0 | nCos = cos( nRealOrient ); |
4950 | | |
4951 | | // tdf#143377 new strategy: instead of using zero for nSin, which |
4952 | | // would be the *correct* value, continue with the corrected maximum |
4953 | | // allowed value which is then *not* zero. This is similar to |
4954 | | // the behaviour before where (just due to numerical unprecisions) |
4955 | | // nSin was also not zero (pure coincidence), but very close to it. |
4956 | | // I checked and tried to make safe all places below that use |
4957 | | // nSin and divide by it, but there is too much going on and that |
4958 | | // would not be safe, so rely on the same values as before, but |
4959 | | // now numerically limited to not get the Skew go havoc |
4960 | 0 | nSin = sin( nRealOrient ); |
4961 | 0 | } |
4962 | 0 | } |
4963 | |
|
4964 | 0 | Size aPaperSize( 1000000, 1000000 ); |
4965 | 0 | if (eOrient==SvxCellOrientation::Stacked) |
4966 | 0 | aPaperSize.setWidth( nOutWidth ); // to center |
4967 | 0 | else if (bBreak) |
4968 | 0 | { |
4969 | 0 | if (nAttrRotate) |
4970 | 0 | { |
4971 | | //! the correct paper size for break depends on the number |
4972 | | //! of rows, as long as the rows can not be outputted individually |
4973 | | //! offsetted -> therefore unlimited, so no wrapping. |
4974 | | //! With offset rows the following would be correct: |
4975 | 0 | aPaperSize.setWidth( static_cast<tools::Long>(nOutHeight / fabs(nSin)) ); |
4976 | 0 | } |
4977 | 0 | else if (eOrient == SvxCellOrientation::Standard) |
4978 | 0 | aPaperSize.setWidth( nOutWidth ); |
4979 | 0 | else |
4980 | 0 | aPaperSize.setWidth( nOutHeight - 1 ); |
4981 | 0 | } |
4982 | 0 | if (bPixelToLogic) |
4983 | 0 | mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
4984 | 0 | else |
4985 | 0 | mxOutputEditEngine->SetPaperSize(aPaperSize); // scale is always 1 |
4986 | | |
4987 | | // read data from cell |
4988 | |
|
4989 | 0 | if (aCell.getType() == CELLTYPE_EDIT) |
4990 | 0 | { |
4991 | 0 | if (aCell.getEditText()) |
4992 | 0 | mxOutputEditEngine->SetTextCurrentDefaults(*aCell.getEditText()); |
4993 | 0 | else |
4994 | 0 | { |
4995 | 0 | OSL_FAIL("pData == 0"); |
4996 | 0 | } |
4997 | 0 | } |
4998 | 0 | else |
4999 | 0 | { |
5000 | 0 | sal_uInt32 nFormat = pPattern->GetNumberFormat( |
5001 | 0 | mpDoc->GetFormatTable(), pCondSet ); |
5002 | 0 | const Color* pColor; |
5003 | 0 | OUString aString = ScCellFormat::GetString( aCell, |
5004 | 0 | nFormat, &pColor, |
5005 | 0 | nullptr, |
5006 | 0 | *mpDoc, |
5007 | 0 | mbShowNullValues, |
5008 | 0 | mbShowFormulas); |
5009 | |
|
5010 | 0 | mxOutputEditEngine->SetTextCurrentDefaults(aString); |
5011 | 0 | if ( pColor && !mbSyntaxMode && !( mbUseStyleColor && mbForceAutoColor ) ) |
5012 | 0 | lcl_SetEditColor( *mxOutputEditEngine, *pColor ); |
5013 | 0 | } |
5014 | |
|
5015 | 0 | if ( mbSyntaxMode ) |
5016 | 0 | { |
5017 | 0 | SetEditSyntaxColor(*mxOutputEditEngine, aCell); |
5018 | 0 | } |
5019 | 0 | else if ( mbUseStyleColor && mbForceAutoColor ) |
5020 | 0 | lcl_SetEditColor( *mxOutputEditEngine, COL_AUTO ); //! or have a flag at EditEngine |
5021 | |
|
5022 | 0 | mxOutputEditEngine->SetUpdateLayout( true ); // after SetText, before CalcTextWidth/GetTextHeight |
5023 | |
|
5024 | 0 | tools::Long nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth()); |
5025 | 0 | tools::Long nEngineHeight = mxOutputEditEngine->GetTextHeight(); |
5026 | |
|
5027 | 0 | if (nAttrRotate && bBreak) |
5028 | 0 | { |
5029 | 0 | double nAbsCos = fabs( nCos ); |
5030 | 0 | double nAbsSin = fabs( nSin ); |
5031 | | |
5032 | | // adjust width of papersize for height of text |
5033 | 0 | int nSteps = 5; |
5034 | 0 | while (nSteps > 0) |
5035 | 0 | { |
5036 | | // everything is in pixels |
5037 | 0 | tools::Long nEnginePixel = mpRefDevice->LogicToPixel( |
5038 | 0 | Size(0,nEngineHeight)).Height(); |
5039 | 0 | tools::Long nEffHeight = nOutHeight - static_cast<tools::Long>(nEnginePixel * nAbsCos) + 2; |
5040 | 0 | tools::Long nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2; |
5041 | 0 | bool bFits = ( nNewWidth >= aPaperSize.Width() ); |
5042 | 0 | if ( bFits ) |
5043 | 0 | nSteps = 0; |
5044 | 0 | else |
5045 | 0 | { |
5046 | 0 | if ( nNewWidth < 4 ) |
5047 | 0 | { |
5048 | | // can't fit -> fall back to using half height |
5049 | 0 | nEffHeight = nOutHeight / 2; |
5050 | 0 | nNewWidth = static_cast<tools::Long>(nEffHeight / nAbsSin) + 2; |
5051 | 0 | nSteps = 0; |
5052 | 0 | } |
5053 | 0 | else |
5054 | 0 | --nSteps; |
5055 | | |
5056 | | // set paper width and get new text height |
5057 | 0 | aPaperSize.setWidth( nNewWidth ); |
5058 | 0 | if (bPixelToLogic) |
5059 | 0 | mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
5060 | 0 | else |
5061 | 0 | mxOutputEditEngine->SetPaperSize(aPaperSize); // Scale is always 1 |
5062 | | //mxOutputEditEngine->QuickFormatDoc( sal_True ); |
5063 | |
|
5064 | 0 | nEngineWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth()); |
5065 | 0 | nEngineHeight = mxOutputEditEngine->GetTextHeight(); |
5066 | 0 | } |
5067 | 0 | } |
5068 | 0 | } |
5069 | |
|
5070 | 0 | tools::Long nRealWidth = nEngineWidth; |
5071 | 0 | tools::Long nRealHeight = nEngineHeight; |
5072 | | |
5073 | | // when rotated, adjust size |
5074 | 0 | if (nAttrRotate) |
5075 | 0 | { |
5076 | 0 | double nAbsCos = fabs( nCos ); |
5077 | 0 | double nAbsSin = fabs( nSin ); |
5078 | |
|
5079 | 0 | if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) |
5080 | 0 | nEngineWidth = static_cast<tools::Long>( nRealWidth * nAbsCos + |
5081 | 0 | nRealHeight * nAbsSin ); |
5082 | 0 | else |
5083 | 0 | nEngineWidth = static_cast<tools::Long>( nRealHeight / nAbsSin ); |
5084 | | //! limit !!! |
5085 | |
|
5086 | 0 | nEngineHeight = static_cast<tools::Long>( nRealHeight * nAbsCos + |
5087 | 0 | nRealWidth * nAbsSin ); |
5088 | 0 | } |
5089 | |
|
5090 | 0 | if (!nAttrRotate) // only rotated text here |
5091 | 0 | bHidden = true; //! check first !!! |
5092 | | |
5093 | | //! omit which doesn't stick out |
5094 | |
|
5095 | 0 | if (!bHidden) |
5096 | 0 | { |
5097 | 0 | Size aClipSize( mnScrX+mnScrW-nStartX, mnScrY+mnScrH-nStartY ); |
5098 | | |
5099 | | // go on writing |
5100 | |
|
5101 | 0 | Size aCellSize; |
5102 | 0 | if (bPixelToLogic) |
5103 | 0 | aCellSize = mpRefDevice->PixelToLogic( Size( nOutWidth, nOutHeight ) ); |
5104 | 0 | else |
5105 | 0 | aCellSize = Size( nOutWidth, nOutHeight ); // scale is one |
5106 | |
|
5107 | 0 | tools::Long nGridWidth = nEngineWidth; |
5108 | 0 | bool bNegative = false; |
5109 | 0 | if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) |
5110 | 0 | { |
5111 | 0 | nGridWidth = aCellSize.Width() + |
5112 | 0 | std::abs(static_cast<tools::Long>( aCellSize.Height() * nCos / nSin )); |
5113 | 0 | bNegative = ( pInfo->nRotateDir == ScRotateDir::Left ); |
5114 | 0 | if ( mbLayoutRTL ) |
5115 | 0 | bNegative = !bNegative; |
5116 | 0 | } |
5117 | | |
5118 | | // use GetOutputArea to hide the grid |
5119 | | // (clip region is done manually below) |
5120 | 0 | OutputAreaParam aAreaParam; |
5121 | |
|
5122 | 0 | SCCOL nCellX = nX; |
5123 | 0 | SCROW nCellY = nY; |
5124 | 0 | SvxCellHorJustify eOutHorJust = eHorJust; |
5125 | 0 | if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) |
5126 | 0 | eOutHorJust = bNegative ? SvxCellHorJustify::Right : SvxCellHorJustify::Left; |
5127 | 0 | tools::Long nNeededWidth = nGridWidth; // in pixel for GetOutputArea |
5128 | 0 | if ( bPixelToLogic ) |
5129 | 0 | nNeededWidth = mpRefDevice->LogicToPixel(Size(nNeededWidth,0)).Width(); |
5130 | |
|
5131 | 0 | GetOutputArea( nX, nArrY, nCellStartX, nPosY, nCellX, nCellY, nNeededWidth, |
5132 | 0 | *pPattern, sal::static_int_cast<sal_uInt16>(eOutHorJust), |
5133 | 0 | false, false, true, aAreaParam ); |
5134 | |
|
5135 | 0 | if ( bShrink ) |
5136 | 0 | { |
5137 | 0 | tools::Long nPixelWidth = bPixelToLogic ? |
5138 | 0 | mpRefDevice->LogicToPixel(Size(nEngineWidth,0)).Width() : nEngineWidth; |
5139 | 0 | tools::Long nNeededPixel = nPixelWidth + nLeftM + nRightM; |
5140 | |
|
5141 | 0 | aAreaParam.mbLeftClip = aAreaParam.mbRightClip = true; |
5142 | | |
5143 | | // always do height |
5144 | 0 | ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM, |
5145 | 0 | false, eOrient, nAttrRotate, bPixelToLogic, |
5146 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
5147 | |
|
5148 | 0 | if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) |
5149 | 0 | { |
5150 | | // do width only if rotating within the cell (standard mode) |
5151 | 0 | ShrinkEditEngine( *mxOutputEditEngine, aAreaParam.maAlignRect, nLeftM, nTopM, nRightM, nBottomM, |
5152 | 0 | true, eOrient, nAttrRotate, bPixelToLogic, |
5153 | 0 | nEngineWidth, nEngineHeight, nNeededPixel, aAreaParam.mbLeftClip, aAreaParam.mbRightClip ); |
5154 | 0 | } |
5155 | | |
5156 | | // nEngineWidth/nEngineHeight is updated in ShrinkEditEngine |
5157 | | // (but width is only valid for standard mode) |
5158 | 0 | nRealWidth = static_cast<tools::Long>(mxOutputEditEngine->CalcTextWidth()); |
5159 | 0 | nRealHeight = mxOutputEditEngine->GetTextHeight(); |
5160 | |
|
5161 | 0 | if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) |
5162 | 0 | nEngineWidth = static_cast<tools::Long>( nRealHeight / fabs( nSin ) ); |
5163 | 0 | } |
5164 | |
|
5165 | 0 | tools::Long nClipStartX = nStartX; |
5166 | 0 | if (nX<mnX1) |
5167 | 0 | { |
5168 | | //! clipping is not needed when on the left side of the window |
5169 | |
|
5170 | 0 | if (nStartX<mnScrX) |
5171 | 0 | { |
5172 | 0 | tools::Long nDif = mnScrX - nStartX; |
5173 | 0 | nClipStartX = mnScrX; |
5174 | 0 | aClipSize.AdjustWidth( -nDif ); |
5175 | 0 | } |
5176 | 0 | } |
5177 | |
|
5178 | 0 | tools::Long nClipStartY = nStartY; |
5179 | 0 | if (nArrY==0 && nClipStartY < nRowPosY ) |
5180 | 0 | { |
5181 | 0 | tools::Long nDif = nRowPosY - nClipStartY; |
5182 | 0 | nClipStartY = nRowPosY; |
5183 | 0 | aClipSize.AdjustHeight( -nDif ); |
5184 | 0 | } |
5185 | |
|
5186 | 0 | if ( nAttrRotate /* && eRotMode != SVX_ROTATE_MODE_STANDARD */ ) |
5187 | 0 | { |
5188 | | // only clip rotated output text at the page border |
5189 | 0 | nClipStartX = mnScrX; |
5190 | 0 | aClipSize.setWidth( mnScrW ); |
5191 | 0 | } |
5192 | |
|
5193 | 0 | if (bPixelToLogic) |
5194 | 0 | aAreaParam.maClipRect = mpRefDevice->PixelToLogic( tools::Rectangle( |
5195 | 0 | Point(nClipStartX,nClipStartY), aClipSize ) ); |
5196 | 0 | else |
5197 | 0 | aAreaParam.maClipRect = tools::Rectangle(Point(nClipStartX, nClipStartY), |
5198 | 0 | aClipSize ); // Scale = 1 |
5199 | |
|
5200 | 0 | if (mbMetaFile) |
5201 | 0 | { |
5202 | 0 | mpDev->Push(); |
5203 | 0 | mpDev->IntersectClipRegion( aAreaParam.maClipRect ); |
5204 | 0 | } |
5205 | 0 | else |
5206 | 0 | mpDev->SetClipRegion( vcl::Region( aAreaParam.maClipRect ) ); |
5207 | |
|
5208 | 0 | Point aLogicStart; |
5209 | 0 | if (bPixelToLogic) |
5210 | 0 | aLogicStart = mpRefDevice->PixelToLogic( Point(nStartX,nStartY) ); |
5211 | 0 | else |
5212 | 0 | aLogicStart = Point(nStartX, nStartY); |
5213 | 0 | if ( eOrient!=SvxCellOrientation::Standard || !bBreak ) |
5214 | 0 | { |
5215 | 0 | tools::Long nAvailWidth = aCellSize.Width(); |
5216 | 0 | if (meType==OUTTYPE_WINDOW && |
5217 | 0 | eOrient!=SvxCellOrientation::Stacked && |
5218 | 0 | pInfo->bAutoFilter) |
5219 | 0 | { |
5220 | | // filter drop-down width depends on row height |
5221 | 0 | double fZoom = mpRefDevice ? mpRefDevice->GetMapMode().GetScaleY() : 1.0; |
5222 | 0 | fZoom = fZoom > 1.0 ? fZoom : 1.0; |
5223 | 0 | if (bPixelToLogic) |
5224 | 0 | nAvailWidth -= mpRefDevice->PixelToLogic(Size(0,fZoom * DROPDOWN_BITMAP_SIZE)).Height(); |
5225 | 0 | else |
5226 | 0 | nAvailWidth -= fZoom * DROPDOWN_BITMAP_SIZE; |
5227 | 0 | tools::Long nComp = nEngineWidth; |
5228 | 0 | if (nAvailWidth<nComp) nAvailWidth=nComp; |
5229 | 0 | } |
5230 | | |
5231 | | // horizontal orientation |
5232 | |
|
5233 | 0 | if (eOrient==SvxCellOrientation::Standard && !nAttrRotate) |
5234 | 0 | { |
5235 | 0 | if (eHorJust==SvxCellHorJustify::Right || |
5236 | 0 | eHorJust==SvxCellHorJustify::Center) |
5237 | 0 | { |
5238 | 0 | mxOutputEditEngine->SetUpdateLayout( false ); |
5239 | |
|
5240 | 0 | SvxAdjust eSvxAdjust = |
5241 | 0 | (eHorJust==SvxCellHorJustify::Right) ? |
5242 | 0 | SvxAdjust::Right : SvxAdjust::Center; |
5243 | 0 | mxOutputEditEngine->SetDefaultItem( |
5244 | 0 | SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); |
5245 | |
|
5246 | 0 | aPaperSize.setWidth( nOutWidth ); |
5247 | 0 | if (bPixelToLogic) |
5248 | 0 | mxOutputEditEngine->SetPaperSize(mpRefDevice->PixelToLogic(aPaperSize)); |
5249 | 0 | else |
5250 | 0 | mxOutputEditEngine->SetPaperSize(aPaperSize); |
5251 | |
|
5252 | 0 | mxOutputEditEngine->SetUpdateLayout( true ); |
5253 | 0 | } |
5254 | 0 | } |
5255 | 0 | else |
5256 | 0 | { |
5257 | | // rotated text is centered by default |
5258 | 0 | if (eHorJust==SvxCellHorJustify::Right) |
5259 | 0 | aLogicStart.AdjustX(nAvailWidth - nEngineWidth ); |
5260 | 0 | else if (eHorJust==SvxCellHorJustify::Center || |
5261 | 0 | eHorJust==SvxCellHorJustify::Standard) |
5262 | 0 | aLogicStart.AdjustX((nAvailWidth - nEngineWidth) / 2 ); |
5263 | 0 | } |
5264 | 0 | } |
5265 | |
|
5266 | 0 | if ( mbLayoutRTL ) |
5267 | 0 | { |
5268 | 0 | if (bPixelToLogic) |
5269 | 0 | aLogicStart.AdjustX( -(mpRefDevice->PixelToLogic( |
5270 | 0 | Size( nCellWidth, 0 ) ).Width()) ); |
5271 | 0 | else |
5272 | 0 | aLogicStart.AdjustX( -nCellWidth ); |
5273 | 0 | } |
5274 | |
|
5275 | 0 | if ( eOrient==SvxCellOrientation::Standard || |
5276 | 0 | eOrient==SvxCellOrientation::Stacked || !bBreak ) |
5277 | 0 | { |
5278 | 0 | if (eVerJust==SvxCellVerJustify::Bottom || |
5279 | 0 | eVerJust==SvxCellVerJustify::Standard) |
5280 | 0 | { |
5281 | 0 | if (bPixelToLogic) |
5282 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0, |
5283 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
5284 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height() |
5285 | 0 | )).Height() ); |
5286 | 0 | else |
5287 | 0 | aLogicStart.AdjustY(aCellSize.Height() - nEngineHeight ); |
5288 | 0 | } |
5289 | | |
5290 | 0 | else if (eVerJust==SvxCellVerJustify::Center) |
5291 | 0 | { |
5292 | 0 | if (bPixelToLogic) |
5293 | 0 | aLogicStart.AdjustY(mpRefDevice->PixelToLogic( Size(0,( |
5294 | 0 | mpRefDevice->LogicToPixel(aCellSize).Height() - |
5295 | 0 | mpRefDevice->LogicToPixel(Size(0,nEngineHeight)).Height()) |
5296 | 0 | / 2)).Height() ); |
5297 | 0 | else |
5298 | 0 | aLogicStart.AdjustY((aCellSize.Height() - nEngineHeight) / 2 ); |
5299 | 0 | } |
5300 | 0 | } |
5301 | | |
5302 | | // TOPBOTTOM and BOTTOMTOP are handled in DrawStrings/DrawEdit |
5303 | 0 | OSL_ENSURE( eOrient == SvxCellOrientation::Standard && nAttrRotate, |
5304 | 0 | "DrawRotated: no rotation" ); |
5305 | |
|
5306 | 0 | Degree10 nOriVal = 0_deg10; |
5307 | 0 | if ( nAttrRotate ) |
5308 | 0 | { |
5309 | | // attribute is 1/100, Font 1/10 degrees |
5310 | 0 | nOriVal = to<Degree10>(nAttrRotate); |
5311 | |
|
5312 | 0 | double nAddX = 0.0; |
5313 | 0 | double nAddY = 0.0; |
5314 | 0 | if ( nCos > 0.0 && eRotMode != SVX_ROTATE_MODE_STANDARD ) |
5315 | 0 | { |
5316 | | //! limit !!! |
5317 | 0 | double nH = nRealHeight * nCos; |
5318 | 0 | nAddX += nH * ( nCos / fabs(nSin) ); |
5319 | 0 | } |
5320 | 0 | if ( nCos < 0.0 && eRotMode == SVX_ROTATE_MODE_STANDARD ) |
5321 | 0 | nAddX -= nRealWidth * nCos; |
5322 | 0 | if ( nSin < 0.0 ) |
5323 | 0 | nAddX -= nRealHeight * nSin; |
5324 | 0 | if ( nSin > 0.0 ) |
5325 | 0 | nAddY += nRealWidth * nSin; |
5326 | 0 | if ( nCos < 0.0 ) |
5327 | 0 | nAddY -= nRealHeight * nCos; |
5328 | |
|
5329 | 0 | if ( eRotMode != SVX_ROTATE_MODE_STANDARD ) |
5330 | 0 | { |
5331 | | //! limit !!! |
5332 | 0 | double nSkew = nTotalHeight * nCos / fabs(nSin); |
5333 | 0 | if ( eRotMode == SVX_ROTATE_MODE_CENTER ) |
5334 | 0 | nAddX -= nSkew * 0.5; |
5335 | 0 | if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nSin > 0.0 ) || |
5336 | 0 | ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nSin < 0.0 ) ) |
5337 | 0 | nAddX -= nSkew; |
5338 | |
|
5339 | 0 | tools::Long nUp = 0; |
5340 | 0 | if ( eVerJust == SvxCellVerJustify::Center ) |
5341 | 0 | nUp = ( aCellSize.Height() - nEngineHeight ) / 2; |
5342 | 0 | else if ( eVerJust == SvxCellVerJustify::Top ) |
5343 | 0 | { |
5344 | 0 | if ( nSin > 0.0 ) |
5345 | 0 | nUp = aCellSize.Height() - nEngineHeight; |
5346 | 0 | } |
5347 | 0 | else // BOTTOM / STANDARD |
5348 | 0 | { |
5349 | 0 | if ( nSin < 0.0 ) |
5350 | 0 | nUp = aCellSize.Height() - nEngineHeight; |
5351 | 0 | } |
5352 | 0 | if ( nUp ) |
5353 | 0 | nAddX += ( nUp * nCos / fabs(nSin) ); |
5354 | 0 | } |
5355 | |
|
5356 | 0 | aLogicStart.AdjustX(static_cast<tools::Long>(nAddX) ); |
5357 | 0 | aLogicStart.AdjustY(static_cast<tools::Long>(nAddY) ); |
5358 | 0 | } |
5359 | | |
5360 | | // bSimClip is not used here (because nOriVal is set) |
5361 | |
|
5362 | 0 | mxOutputEditEngine->DrawText_ToPosition(*mpDev, aLogicStart, nOriVal); |
5363 | |
|
5364 | 0 | if (mbMetaFile) |
5365 | 0 | mpDev->Pop(); |
5366 | 0 | else |
5367 | 0 | mpDev->SetClipRegion(); |
5368 | 0 | } |
5369 | 0 | } |
5370 | 0 | } |
5371 | 0 | } |
5372 | 0 | nPosX += mpRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign; |
5373 | 0 | } |
5374 | 0 | } |
5375 | 0 | nRowPosY += mpRowInfo[nArrY].nHeight; |
5376 | 0 | } |
5377 | 0 | } |
5378 | | |
5379 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |