xref: /core/sc/source/ui/view/output.cxx (revision bd763848)
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/brushitem.hxx>
22 #include <svtools/colorcfg.hxx>
23 #include <svx/rotmodit.hxx>
24 #include <editeng/shaditem.hxx>
25 #include <editeng/svxfont.hxx>
26 #include <tools/poly.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/pdfextoutdevdata.hxx>
29 #include <svx/framelinkarray.hxx>
30 #include <drawinglayer/geometry/viewinformation2d.hxx>
31 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
32 #include <drawinglayer/processor2d/processor2dtools.hxx>
33 #include <officecfg/Office/Common.hxx>
34 #include <officecfg/Office/Calc.hxx>
35 #include <vcl/lineinfo.hxx>
36 #include <vcl/gradient.hxx>
37 #include <vcl/settings.hxx>
38 #include <vcl/pdf/PDFNote.hxx>
39 #include <svx/unoapi.hxx>
40 #include <sal/log.hxx>
41 #include <comphelper/lok.hxx>
42 #include <o3tl/unit_conversion.hxx>
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 
45 #include <output.hxx>
46 #include <document.hxx>
47 #include <drwlayer.hxx>
48 #include <formulacell.hxx>
49 #include <attrib.hxx>
50 #include <patattr.hxx>
51 #include <progress.hxx>
52 #include <pagedata.hxx>
53 #include <chgtrack.hxx>
54 #include <chgviset.hxx>
55 #include <viewutil.hxx>
56 #include <gridmerg.hxx>
57 #include <fillinfo.hxx>
58 #include <scmod.hxx>
59 #include <appoptio.hxx>
60 #include <postit.hxx>
61 #include <validat.hxx>
62 #include <detfunc.hxx>
63 #include <editutil.hxx>
64 
65 #include <SparklineRenderer.hxx>
66 #include <colorscale.hxx>
67 
68 #include <math.h>
69 #include <memory>
70 
71 using namespace com::sun::star;
72 
73 // Static Data
74 
75 // color for ChangeTracking "by author" as in the writer (swmodul1.cxx)
76 
77 #define SC_AUTHORCOLORCOUNT     9
78 
79 const Color nAuthorColor[ SC_AUTHORCOLORCOUNT ] = {
80                     COL_LIGHTRED,       COL_LIGHTBLUE,      COL_LIGHTMAGENTA,
81                     COL_GREEN,          COL_RED,            COL_BLUE,
82                     COL_BROWN,          COL_MAGENTA,        COL_CYAN };
83 
84 // Helper class for color assignment to avoid repeated lookups for the same user
85 
ScActionColorChanger(const ScChangeTrack & rTrack)86 ScActionColorChanger::ScActionColorChanger( const ScChangeTrack& rTrack ) :
87     rOpt( SC_MOD()->GetAppOptions() ),
88     rUsers( rTrack.GetUserCollection() ),
89     nLastUserIndex( 0 ),
90     nColor( COL_BLACK )
91 {
92 }
93 
Update(const ScChangeAction & rAction)94 void ScActionColorChanger::Update( const ScChangeAction& rAction )
95 {
96     Color nSetColor;
97     switch (rAction.GetType())
98     {
99         case SC_CAT_INSERT_COLS:
100         case SC_CAT_INSERT_ROWS:
101         case SC_CAT_INSERT_TABS:
102             nSetColor = rOpt.GetTrackInsertColor();
103             break;
104         case SC_CAT_DELETE_COLS:
105         case SC_CAT_DELETE_ROWS:
106         case SC_CAT_DELETE_TABS:
107             nSetColor = rOpt.GetTrackDeleteColor();
108             break;
109         case SC_CAT_MOVE:
110             nSetColor = rOpt.GetTrackMoveColor();
111             break;
112         default:
113             nSetColor = rOpt.GetTrackContentColor();
114             break;
115     }
116     if ( nSetColor != COL_TRANSPARENT )     // color assigned
117         nColor = nSetColor;
118     else                                    // by author
119     {
120         if (aLastUserName != rAction.GetUser())
121         {
122             aLastUserName = rAction.GetUser();
123             std::set<OUString>::const_iterator it = rUsers.find(aLastUserName);
124             if (it == rUsers.end())
125             {
126                 // empty string is possible if a name wasn't found while saving a 5.0 file
127                 SAL_INFO_IF( aLastUserName.isEmpty(), "sc.ui", "Author not found" );
128                 nLastUserIndex = 0;
129             }
130             else
131             {
132                 size_t nPos = std::distance(rUsers.begin(), it);
133                 nLastUserIndex = nPos % SC_AUTHORCOLORCOUNT;
134             }
135         }
136         nColor = nAuthorColor[nLastUserIndex];
137     }
138 }
139 
ScOutputData(OutputDevice * pNewDev,ScOutputType eNewType,ScTableInfo & rTabInfo,ScDocument * pNewDoc,SCTAB nNewTab,tools::Long nNewScrX,tools::Long nNewScrY,SCCOL nNewX1,SCROW nNewY1,SCCOL nNewX2,SCROW nNewY2,double nPixelPerTwipsX,double nPixelPerTwipsY,const Fraction * pZoomX,const Fraction * pZoomY)140 ScOutputData::ScOutputData( OutputDevice* pNewDev, ScOutputType eNewType,
141                             ScTableInfo& rTabInfo, ScDocument* pNewDoc,
142                             SCTAB nNewTab, tools::Long nNewScrX, tools::Long nNewScrY,
143                             SCCOL nNewX1, SCROW nNewY1, SCCOL nNewX2, SCROW nNewY2,
144                             double nPixelPerTwipsX, double nPixelPerTwipsY,
145                             const Fraction* pZoomX, const Fraction* pZoomY ) :
146     mpOriginalTargetDevice( pNewDev ),
147     mpDev( pNewDev ),
148     mpRefDevice( pNewDev ),      // default is output device
149     pFmtDevice( pNewDev ),      // default is output device
150     mrTabInfo( rTabInfo ),
151     pRowInfo( rTabInfo.mpRowInfo.get() ),
152     nArrCount( rTabInfo.mnArrCount ),
153     mpDoc( pNewDoc ),
154     nTab( nNewTab ),
155     nScrX( nNewScrX ),
156     nScrY( nNewScrY ),
157     nX1( nNewX1 ),
158     nY1( nNewY1 ),
159     nX2( nNewX2 ),
160     nY2( nNewY2 ),
161     eType( eNewType ),
162     mnPPTX( nPixelPerTwipsX ),
163     mnPPTY( nPixelPerTwipsY ),
164     pViewShell( nullptr ),
165     pDrawView( nullptr ),
166     bEditMode( false ),
167     nEditCol( 0 ),
168     nEditRow( 0 ),
169     bMetaFile( false ),
170     bPagebreakMode( false ),
171     bSolidBackground( false ),
172     mbUseStyleColor( false ),
173     mbForceAutoColor( officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get() ),
174     mbSyntaxMode( false ),
175     aGridColor( COL_BLACK ),
176     mbShowNullValues( true ),
177     mbShowFormulas( false ),
178     bShowSpellErrors( false ),
179     bMarkClipped( false ), // sal_False for printer/metafile etc.
180     bSnapPixel( false ),
181     bAnyClipped( false ),
182     bVertical(false),
183     mpTargetPaintWindow(nullptr), // #i74769# use SdrPaintWindow direct
184     mpSpellCheckCxt(nullptr)
185 {
186     if (pZoomX)
187         aZoomX = *pZoomX;
188     else
189         aZoomX = Fraction(1,1);
190     if (pZoomY)
191         aZoomY = *pZoomY;
192     else
193         aZoomY = Fraction(1,1);
194 
195     nVisX1 = nX1;
196     nVisY1 = nY1;
197     nVisX2 = nX2;
198     nVisY2 = nY2;
199     mpDoc->StripHidden( nVisX1, nVisY1, nVisX2, nVisY2, nTab );
200 
201     nScrW = 0;
202     for (SCCOL nX=nVisX1; nX<=nVisX2; nX++)
203         nScrW += pRowInfo[0].basicCellInfo(nX).nWidth;
204 
205     nMirrorW = nScrW;
206 
207     nScrH = 0;
208     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
209         nScrH += pRowInfo[nArrY].nHeight;
210 
211     bTabProtected = mpDoc->IsTabProtected( nTab );
212     bLayoutRTL = mpDoc->IsLayoutRTL( nTab );
213 
214     // always needed, so call at the end of the constructor
215     SetCellRotations();
216     InitOutputEditEngine();
217 }
218 
~ScOutputData()219 ScOutputData::~ScOutputData()
220 {
221 }
222 
SetSpellCheckContext(const sc::SpellCheckContext * pCxt)223 void ScOutputData::SetSpellCheckContext( const sc::SpellCheckContext* pCxt )
224 {
225     mpSpellCheckCxt = pCxt;
226 }
227 
SetContentDevice(OutputDevice * pContentDev)228 void ScOutputData::SetContentDevice( OutputDevice* pContentDev )
229 {
230     // use pContentDev instead of pDev where used
231 
232     if ( mpRefDevice == mpDev )
233         mpRefDevice = pContentDev;
234     if ( pFmtDevice == mpDev )
235         pFmtDevice = pContentDev;
236     mpDev = pContentDev;
237 }
238 
SetMirrorWidth(tools::Long nNew)239 void ScOutputData::SetMirrorWidth( tools::Long nNew )
240 {
241     nMirrorW = nNew;
242 }
243 
SetGridColor(const Color & rColor)244 void ScOutputData::SetGridColor( const Color& rColor )
245 {
246     aGridColor = rColor;
247 }
248 
SetMarkClipped(bool bSet)249 void ScOutputData::SetMarkClipped( bool bSet )
250 {
251     bMarkClipped = bSet;
252 }
253 
SetShowNullValues(bool bSet)254 void ScOutputData::SetShowNullValues( bool bSet )
255 {
256     mbShowNullValues = bSet;
257 }
258 
SetShowFormulas(bool bSet)259 void ScOutputData::SetShowFormulas( bool bSet )
260 {
261     mbShowFormulas = bSet;
262 }
263 
SetShowSpellErrors(bool bSet)264 void ScOutputData::SetShowSpellErrors( bool bSet )
265 {
266     bShowSpellErrors = bSet;
267     // reset EditEngine because it depends on bShowSpellErrors
268     mxOutputEditEngine.reset();
269 }
270 
SetSnapPixel()271 void ScOutputData::SetSnapPixel()
272 {
273     bSnapPixel = true;
274 }
275 
SetEditCell(SCCOL nCol,SCROW nRow)276 void ScOutputData::SetEditCell( SCCOL nCol, SCROW nRow )
277 {
278     nEditCol = nCol;
279     nEditRow = nRow;
280     bEditMode = true;
281 }
282 
SetMetaFileMode(bool bNewMode)283 void ScOutputData::SetMetaFileMode( bool bNewMode )
284 {
285     bMetaFile = bNewMode;
286 }
287 
SetSyntaxMode(bool bNewMode)288 void ScOutputData::SetSyntaxMode( bool bNewMode )
289 {
290     mbSyntaxMode = bNewMode;
291     if ( bNewMode && !mxValueColor )
292     {
293         const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
294         mxValueColor = rColorCfg.GetColorValue( svtools::CALCVALUE ).nColor;
295         mxTextColor = rColorCfg.GetColorValue( svtools::CALCTEXT ).nColor;
296         mxFormulaColor = rColorCfg.GetColorValue( svtools::CALCFORMULA ).nColor;
297     }
298 }
299 
ReopenPDFStructureElement(vcl::PDFWriter::StructElement aType,SCROW nRow,SCCOL nCol)300 bool ScOutputData::ReopenPDFStructureElement(vcl::PDFWriter::StructElement aType, SCROW nRow, SCCOL nCol)
301 {
302     bool bReopenTag = false;
303     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
304     if (pPDF)
305     {
306         if (aType == vcl::PDFWriter::Part) // Worksheet
307         {
308             if (pPDF->GetScPDFState()->m_WorksheetId != -1)
309             {
310                 sal_Int32 nId = pPDF->GetScPDFState()->m_WorksheetId;
311                 pPDF->BeginStructureElement(nId);
312                 bReopenTag = true;
313             }
314         }
315         else if (aType == vcl::PDFWriter::Table)
316         {
317             if (pPDF->GetScPDFState()->m_TableId != -1)
318             {
319                 sal_Int32 nId = pPDF->GetScPDFState()->m_TableId;
320                 pPDF->BeginStructureElement(nId);
321                 bReopenTag = true;
322             }
323         }
324         else if (aType == vcl::PDFWriter::TableRow)
325         {
326             const auto aIter = pPDF->GetScPDFState()->m_TableRowMap.find(nRow);
327             if (aIter != pPDF->GetScPDFState()->m_TableRowMap.end() && nRow == aIter->first)
328             {
329                 sal_Int32 nId = (*aIter).second;
330                 pPDF->BeginStructureElement(nId);
331                 bReopenTag = true;
332             }
333         }
334         else if (aType == vcl::PDFWriter::TableData)
335         {
336             const std::pair<SCROW, SCCOL> keyToFind = std::make_pair(nRow, nCol);
337             const auto aIter = pPDF->GetScPDFState()->m_TableDataMap.find(keyToFind);
338             if (aIter != pPDF->GetScPDFState()->m_TableDataMap.end() && keyToFind == aIter->first)
339             {
340                 sal_Int32 nId = (*aIter).second;
341                 pPDF->BeginStructureElement(nId);
342                 bReopenTag = true;
343             }
344         }
345     }
346 
347     return bReopenTag;
348 }
349 
DrawGrid(vcl::RenderContext & rRenderContext,bool bGrid,bool bPage,bool bMergeCover)350 void ScOutputData::DrawGrid(vcl::RenderContext& rRenderContext, bool bGrid, bool bPage, bool bMergeCover)
351 {
352     // bMergeCover : Draw lines in sheet bgcolor to cover lok client grid lines in merged cell areas.
353     // When scNoGridBackground is set in lok mode, bMergeCover is set to true and bGrid to false.
354 
355     SCCOL nX;
356     SCROW nY;
357     tools::Long nPosX;
358     tools::Long nPosY;
359     SCSIZE nArrY;
360     ScBreakType nBreak    = ScBreakType::NONE;
361     ScBreakType nBreakOld = ScBreakType::NONE;
362 
363     bool bSingle;
364     bool bDashed = false;
365     Color aPageColor;
366     Color aManualColor;
367 
368     if (bPagebreakMode)
369         bPage = false;          // no "normal" breaks over the whole width/height
370 
371     // It is a big mess to distinguish when we are using pixels and when logic
372     // units for drawing.  Ultimately we want to work only in the logic units,
373     // but until that happens, we need to special-case:
374     //
375     //   * metafile
376     //   * drawing to the screen - everything is internally counted in pixels there
377     //
378     // 'Internally' in the above means the pCellInfo[...].nWidth and
379     // pRowInfo[...]->nHeight:
380     //
381     //   * when bWorksInPixels is true: these are in pixels
382     //   * when bWorksInPixels is false: these are in the logic units
383     //
384     // This is where all the confusion comes from, ultimately we want them
385     // always in the logic units (100th of millimeters), but we need to get
386     // there gradually (get rid of setting MapUnit::MapPixel first), otherwise we'd
387     // break all the drawing by one change.
388     // So until that happens, we need to special case.
389     bool bWorksInPixels = bMetaFile;
390     const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
391     Color aSheetBGColor = rColorCfg.GetColorValue(::svtools::DOCCOLOR).nColor;
392 
393     if ( eType == OUTTYPE_WINDOW )
394     {
395         bWorksInPixels = true;
396         aPageColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKAUTOMATIC).nColor;
397         aManualColor = rColorCfg.GetColorValue(svtools::CALCPAGEBREAKMANUAL).nColor;
398     }
399     else
400     {
401         aPageColor = aGridColor;
402         aManualColor = aGridColor;
403     }
404 
405     tools::Long nOneX = 1;
406     tools::Long nOneY = 1;
407     if (!bWorksInPixels)
408     {
409         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
410         nOneX = aOnePixel.Width();
411         nOneY = aOnePixel.Height();
412     }
413 
414     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
415     tools::Long nSignedOneX = nOneX * nLayoutSign;
416 
417     rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
418 
419     ScGridMerger aGrid(&rRenderContext, nOneX, nOneY);
420 
421     // vertical lines
422 
423     nPosX = nScrX;
424     if ( bLayoutRTL )
425         nPosX += nMirrorW - nOneX;
426 
427     for (nX=nX1; nX<=nX2; nX++)
428     {
429         sal_uInt16 nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
430         if (nWidth)
431         {
432             nPosX += nWidth * nLayoutSign;
433 
434             if ( bPage )
435             {
436                 // Search also in hidden part for page breaks
437                 SCCOL nCol = nX + 1;
438                 while (nCol <= mpDoc->MaxCol())
439                 {
440                     nBreak = mpDoc->HasColBreak(nCol, nTab);
441                     bool bHidden = mpDoc->ColHidden(nCol, nTab);
442 
443                     if ( nBreak != ScBreakType::NONE || !bHidden )
444                         break;
445                     ++nCol;
446                 }
447 
448                 if (nBreak != nBreakOld)
449                 {
450                     aGrid.Flush();
451 
452                     if (static_cast<int>(nBreak))
453                     {
454                         rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
455                                                         aPageColor );
456                         bDashed = true;
457                     }
458                     else
459                     {
460                         rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
461                         bDashed = false;
462                     }
463 
464                     nBreakOld = nBreak;
465                 }
466             }
467 
468             bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover; // simple grid only if set that way
469 
470             sal_uInt16 nWidthXplus1 = pRowInfo[0].basicCellInfo(nX+1).nWidth;
471             bSingle = false; //! get into Fillinfo !!!!!
472             if ( nX<mpDoc->MaxCol() && !bSingle )
473             {
474                 bSingle = ( nWidthXplus1 == 0 );
475                 for (nArrY=1; nArrY+1<nArrCount && !bSingle; nArrY++)
476                 {
477                     if (pRowInfo[nArrY].cellInfo(nX+1).bHOverlapped)
478                         bSingle = true;
479                     if (pRowInfo[nArrY].cellInfo(nX).bHideGrid)
480                         bSingle = true;
481                 }
482             }
483 
484             if (bDraw)
485             {
486                 if ( nX<mpDoc->MaxCol() && bSingle )
487                 {
488                     SCCOL nVisX = nX + 1;
489                     while ( nVisX < mpDoc->MaxCol() && !mpDoc->GetColWidth(nVisX,nTab) )
490                         ++nVisX;
491 
492                     nPosY = nScrY;
493                     for (nArrY=1; nArrY+1<nArrCount; nArrY++)
494                     {
495                         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
496                         const tools::Long nNextY = nPosY + pThisRowInfo->nHeight;
497 
498                         bool bHOver = pThisRowInfo->cellInfo(nX).bHideGrid;
499                         if (!bHOver)
500                         {
501                             if (nWidthXplus1)
502                                 bHOver = pThisRowInfo->cellInfo(nX+1).bHOverlapped;
503                             else
504                             {
505                                 if (nVisX <= nX2)
506                                     bHOver = pThisRowInfo->cellInfo(nVisX).bHOverlapped;
507                                 else
508                                     bHOver = mpDoc->GetAttr(
509                                                 nVisX,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
510                                                 ->IsHorOverlapped();
511                                 if (bHOver)
512                                     bHOver = mpDoc->GetAttr(
513                                                 nX + 1,pThisRowInfo->nRowNo,nTab,ATTR_MERGE_FLAG)
514                                                 ->IsHorOverlapped();
515                             }
516                         }
517 
518                         if ((pThisRowInfo->bChanged && !bHOver && !bMergeCover) || (bHOver && bMergeCover))
519                         {
520                             aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nPosY, nNextY-nOneY, bDashed);
521                         }
522                         nPosY = nNextY;
523                     }
524                 }
525                 else if (!bMergeCover)
526                 {
527                     aGrid.AddVerLine(bWorksInPixels, nPosX-nSignedOneX, nScrY, nScrY+nScrH-nOneY, bDashed);
528                 }
529             }
530         }
531     }
532 
533     // horizontal lines
534 
535     bool bHiddenRow = true;
536     SCROW nHiddenEndRow = -1;
537     nPosY = nScrY;
538     for (nArrY=1; nArrY+1<nArrCount; nArrY++)
539     {
540         SCSIZE nArrYplus1 = nArrY+1;
541         nY = pRowInfo[nArrY].nRowNo;
542         SCROW nYplus1 = nY+1;
543         nPosY += pRowInfo[nArrY].nHeight;
544 
545         if (pRowInfo[nArrY].bChanged)
546         {
547             if ( bPage )
548             {
549                 for (SCROW i = nYplus1; i <= mpDoc->MaxRow(); ++i)
550                 {
551                     if (i > nHiddenEndRow)
552                         bHiddenRow = mpDoc->RowHidden(i, nTab, nullptr, &nHiddenEndRow);
553                     /* TODO: optimize the row break thing for large hidden
554                      * segments where HasRowBreak() has to be called
555                      * nevertheless for each row, as a row break is drawn also
556                      * for hidden rows, above them. This needed to be done only
557                      * once per hidden segment, maybe giving manual breaks
558                      * priority. Something like GetNextRowBreak() and
559                      * GetNextManualRowBreak(). */
560                     nBreak = mpDoc->HasRowBreak(i, nTab);
561                     if (!bHiddenRow || nBreak != ScBreakType::NONE)
562                         break;
563                 }
564 
565                 if (nBreakOld != nBreak)
566                 {
567                     aGrid.Flush();
568 
569                     if (static_cast<int>(nBreak))
570                     {
571                         rRenderContext.SetLineColor( (nBreak & ScBreakType::Manual) ? aManualColor :
572                                                         aPageColor );
573                         bDashed = true;
574                     }
575                     else
576                     {
577                         rRenderContext.SetLineColor(bMergeCover ? aSheetBGColor : aGridColor);
578                         bDashed = false;
579                     }
580 
581                     nBreakOld = nBreak;
582                 }
583             }
584 
585             bool bDraw = bGrid || nBreakOld != ScBreakType::NONE || bMergeCover;    // simple grid only if set so
586 
587             bool bNextYisNextRow = (pRowInfo[nArrYplus1].nRowNo == nYplus1);
588             bSingle = !bNextYisNextRow;             // Hidden
589             for (SCCOL i=nX1; i<=nX2 && !bSingle; i++)
590             {
591                 if (pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped)
592                     bSingle = true;
593             }
594 
595             if (bDraw)
596             {
597                 if ( bSingle && nY<mpDoc->MaxRow() )
598                 {
599                     SCROW nVisY = pRowInfo[nArrYplus1].nRowNo;
600 
601                     nPosX = nScrX;
602                     if ( bLayoutRTL )
603                         nPosX += nMirrorW - nOneX;
604 
605                     for (SCCOL i=nX1; i<=nX2; i++)
606                     {
607                         const tools::Long nNextX = nPosX + pRowInfo[0].basicCellInfo(i).nWidth * nLayoutSign;
608                         if (nNextX != nPosX)                                // visible
609                         {
610                             bool bVOver;
611                             if ( bNextYisNextRow )
612                                 bVOver = pRowInfo[nArrYplus1].cellInfo(i).bVOverlapped;
613                             else
614                             {
615                                 bVOver = mpDoc->GetAttr(
616                                             i,nYplus1,nTab,ATTR_MERGE_FLAG)
617                                             ->IsVerOverlapped()
618                                     &&   mpDoc->GetAttr(
619                                             i,nVisY,nTab,ATTR_MERGE_FLAG)
620                                             ->IsVerOverlapped();
621                                     //! nVisY from Array ??
622                             }
623 
624                             if ((!bVOver && !bMergeCover) || (bVOver && bMergeCover))
625                             {
626                                 aGrid.AddHorLine(bWorksInPixels, nPosX, nNextX-nSignedOneX, nPosY-nOneY, bDashed);
627                             }
628                         }
629                         nPosX = nNextX;
630                     }
631                 }
632                 else if (!bMergeCover)
633                 {
634                     aGrid.AddHorLine(bWorksInPixels, nScrX, nScrX+nScrW-nOneX, nPosY-nOneY, bDashed);
635                 }
636             }
637         }
638     }
639 }
640 
SetPagebreakMode(ScPageBreakData * pPageData)641 void ScOutputData::SetPagebreakMode( ScPageBreakData* pPageData )
642 {
643     bPagebreakMode = true;
644     if (!pPageData)
645         return;                     // not yet initialized -> everything "not printed"
646 
647     // mark printed range
648     // (everything in FillInfo is already initialized to sal_False)
649 
650     sal_uInt16 nRangeCount = sal::static_int_cast<sal_uInt16>(pPageData->GetCount());
651     for (sal_uInt16 nPos=0; nPos<nRangeCount; nPos++)
652     {
653         ScRange aRange = pPageData->GetData( nPos ).GetPrintRange();
654 
655         SCCOL nStartX = std::max( aRange.aStart.Col(), nX1 );
656         SCCOL nEndX   = std::min( aRange.aEnd.Col(),   nX2 );
657         SCROW nStartY = std::max( aRange.aStart.Row(), nY1 );
658         SCROW nEndY   = std::min( aRange.aEnd.Row(),   nY2 );
659 
660         for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
661         {
662             RowInfo* pThisRowInfo = &pRowInfo[nArrY];
663             if ( pThisRowInfo->bChanged && pThisRowInfo->nRowNo >= nStartY &&
664                                            pThisRowInfo->nRowNo <= nEndY )
665             {
666                 for (SCCOL nX=nStartX; nX<=nEndX; nX++)
667                     pThisRowInfo->cellInfo(nX).bPrinted = true;
668             }
669         }
670     }
671 }
672 
SetCellRotations()673 void ScOutputData::SetCellRotations()
674 {
675     //! save nRotMax
676     SCCOL nRotMax = nX2;
677     for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
678         if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
679             nRotMax = pRowInfo[nRotY].nRotMaxCol;
680 
681     for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
682     {
683         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
684         if ( pThisRowInfo->nRotMaxCol != SC_ROTMAX_NONE &&
685              ( pThisRowInfo->bChanged || pRowInfo[nArrY-1].bChanged ||
686                ( nArrY+1<nArrCount && pRowInfo[nArrY+1].bChanged ) ) )
687         {
688             SCROW nY = pThisRowInfo->nRowNo;
689 
690             for (SCCOL nX=0; nX<=nRotMax; nX++)
691             {
692                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
693                 const ScPatternAttr* pPattern = pInfo->pPatternAttr;
694                 const SfxItemSet* pCondSet = pInfo->pConditionSet;
695 
696                 if ( !pPattern && !mpDoc->ColHidden(nX, nTab) )
697                 {
698                     pPattern = mpDoc->GetPattern( nX, nY, nTab );
699                     pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
700                 }
701 
702                 if ( pPattern )     // column isn't hidden
703                 {
704                     ScRotateDir nDir = pPattern->GetRotateDir( pCondSet );
705                     if (nDir != ScRotateDir::NONE)
706                     {
707                         // Needed for ScCellInfo internal decisions (bg fill, ...)
708                         pInfo->nRotateDir = nDir;
709 
710                         // create target coordinates
711                         const SCCOL nTargetX(nX - nVisX1 + 1);
712                         const SCROW nTargetY(nY - nVisY1 + 1);
713 
714                         // Check for values - below in SetCellRotation these will
715                         // be converted to size_t and thus may not be negative
716                         if(nTargetX >= 0 && nTargetY >= 0)
717                         {
718                             // add rotation info to Array information
719                             const Degree100 nAttrRotate(pPattern->GetRotateVal(pCondSet));
720                             const SvxRotateMode eRotMode(pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue());
721                             const double fOrient((bLayoutRTL ? -1.0 : 1.0) * toRadians(nAttrRotate)); // 1/100th degrees -> [0..2PI]
722                             svx::frame::Array& rArray = mrTabInfo.maArray;
723 
724                             rArray.SetCellRotation(nTargetX, nTargetY, eRotMode, fOrient);
725                         }
726                     }
727                 }
728             }
729         }
730     }
731 }
732 
lcl_GetRotateDir(const ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB nTab)733 static ScRotateDir lcl_GetRotateDir( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
734 {
735     const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
736     const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
737 
738     ScRotateDir nRet = ScRotateDir::NONE;
739 
740     Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
741     if ( nAttrRotate )
742     {
743         SvxRotateMode eRotMode =
744                     pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
745 
746         if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
747             nRet = ScRotateDir::Standard;
748         else if ( eRotMode == SVX_ROTATE_MODE_CENTER )
749             nRet = ScRotateDir::Center;
750         else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM )
751         {
752             tools::Long nRot180 = nAttrRotate.get() % 18000;     // 1/100 degree
753             if ( nRot180 == 9000 )
754                 nRet = ScRotateDir::Center;
755             else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) ||
756                       ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) )
757                 nRet = ScRotateDir::Left;
758             else
759                 nRet = ScRotateDir::Right;
760         }
761     }
762 
763     return nRet;
764 }
765 
lcl_FindBackground(const ScDocument * pDoc,SCCOL nCol,SCROW nRow,SCTAB nTab)766 static const SvxBrushItem* lcl_FindBackground( const ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab )
767 {
768     const ScPatternAttr* pPattern = pDoc->GetPattern( nCol, nRow, nTab );
769     const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
770     const SvxBrushItem* pBackground =
771                             &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
772 
773     ScRotateDir nDir = lcl_GetRotateDir( pDoc, nCol, nRow, nTab );
774 
775     // treat CENTER like RIGHT
776     if ( nDir == ScRotateDir::Right || nDir == ScRotateDir::Center )
777     {
778         // text goes to the right -> take background from the left
779         while ( nCol > 0 && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
780                             pBackground->GetColor().GetAlpha() != 0 )
781         {
782             --nCol;
783             pPattern = pDoc->GetPattern( nCol, nRow, nTab );
784             pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
785             pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
786         }
787     }
788     else if ( nDir == ScRotateDir::Left )
789     {
790         // text goes to the left -> take background from the right
791         while ( nCol < pDoc->MaxCol() && lcl_GetRotateDir( pDoc, nCol, nRow, nTab ) == nDir &&
792                             pBackground->GetColor().GetAlpha() != 0 )
793         {
794             ++nCol;
795             pPattern = pDoc->GetPattern( nCol, nRow, nTab );
796             pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
797             pBackground = &pPattern->GetItem( ATTR_BACKGROUND, pCondSet );
798         }
799     }
800 
801     return pBackground;
802 }
803 
lcl_EqualBack(const RowInfo & rFirst,const RowInfo & rOther,SCCOL nX1,SCCOL nX2,bool bShowProt,bool bPagebreakMode)804 static bool lcl_EqualBack( const RowInfo& rFirst, const RowInfo& rOther,
805                     SCCOL nX1, SCCOL nX2, bool bShowProt, bool bPagebreakMode )
806 {
807     if ( rFirst.bChanged   != rOther.bChanged ||
808          rFirst.bEmptyBack != rOther.bEmptyBack )
809         return false;
810 
811     SCCOL nX;
812     if ( bShowProt )
813     {
814         for ( nX=nX1; nX<=nX2; nX++ )
815         {
816             const ScPatternAttr* pPat1 = rFirst.cellInfo(nX).pPatternAttr;
817             const ScPatternAttr* pPat2 = rOther.cellInfo(nX).pPatternAttr;
818             if ( !pPat1 || !pPat2 ||
819                     !SfxPoolItem::areSame(pPat1->GetItem(ATTR_PROTECTION), pPat2->GetItem(ATTR_PROTECTION) ) )
820                 return false;
821         }
822     }
823     else
824     {
825         for ( nX=nX1; nX<=nX2; nX++ )
826             if ( !SfxPoolItem::areSame(rFirst.cellInfo(nX).maBackground.getItem(), rOther.cellInfo(nX).maBackground.getItem() ) )
827                 return false;
828     }
829 
830     if ( rFirst.nRotMaxCol != SC_ROTMAX_NONE || rOther.nRotMaxCol != SC_ROTMAX_NONE )
831         for ( nX=nX1; nX<=nX2; nX++ )
832             if ( rFirst.cellInfo(nX).nRotateDir != rOther.cellInfo(nX).nRotateDir )
833                 return false;
834 
835     if ( bPagebreakMode )
836         for ( nX=nX1; nX<=nX2; nX++ )
837             if ( rFirst.cellInfo(nX).bPrinted != rOther.cellInfo(nX).bPrinted )
838                 return false;
839 
840     for ( nX=nX1; nX<=nX2; nX++ )
841     {
842         std::optional<Color> const & pCol1 = rFirst.cellInfo(nX).mxColorScale;
843         std::optional<Color> const & pCol2 = rOther.cellInfo(nX).mxColorScale;
844         if( (pCol1 && !pCol2) || (!pCol1 && pCol2) )
845             return false;
846 
847         if (pCol1 && (*pCol1 != *pCol2))
848             return false;
849 
850         const ScDataBarInfo* pInfo1 = rFirst.cellInfo(nX).pDataBar;
851         const ScDataBarInfo* pInfo2 = rOther.cellInfo(nX).pDataBar;
852 
853         if( (pInfo1 && !pInfo2) || (!pInfo1 && pInfo2) )
854             return false;
855 
856         if (pInfo1 && (*pInfo1 != *pInfo2))
857             return false;
858 
859         // each cell with an icon set should be painted the same way
860         const ScIconSetInfo* pIconSet1 = rFirst.cellInfo(nX).pIconSet;
861         const ScIconSetInfo* pIconSet2 = rOther.cellInfo(nX).pIconSet;
862 
863         if(pIconSet1 || pIconSet2)
864             return false;
865     }
866 
867     return true;
868 }
869 
DrawDocumentBackground()870 void ScOutputData::DrawDocumentBackground()
871 {
872     if ( !bSolidBackground )
873         return;
874 
875     Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
876     mpDev->SetLineColor(aBgColor);
877     mpDev->SetFillColor(aBgColor);
878 
879     Point aScreenPos  = mpDev->PixelToLogic(Point(nScrX, nScrY));
880     Size  aScreenSize = mpDev->PixelToLogic(Size(nScrW - 1,nScrH - 1));
881 
882     mpDev->DrawRect(tools::Rectangle(aScreenPos, aScreenSize));
883 }
884 
885 namespace {
886 
887 const double lclCornerRectTransparency = 40.0;
888 
drawDataBars(vcl::RenderContext & rRenderContext,const ScDataBarInfo * pOldDataBarInfo,const tools::Rectangle & rRect,tools::Long nOneX,tools::Long nOneY)889 void drawDataBars(vcl::RenderContext& rRenderContext, const ScDataBarInfo* pOldDataBarInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY)
890 {
891     tools::Long nPosZero = 0;
892     tools::Rectangle aPaintRect = rRect;
893     aPaintRect.AdjustTop(2 * nOneY );
894     aPaintRect.AdjustBottom( -(2 * nOneY) );
895     aPaintRect.AdjustLeft( 2 * nOneX );
896     aPaintRect.AdjustRight( -(2 * nOneX) );
897     if(pOldDataBarInfo->mnZero)
898     {
899         // need to calculate null point in cell
900         tools::Long nLength = aPaintRect.Right() - aPaintRect.Left();
901         nPosZero = static_cast<tools::Long>(aPaintRect.Left() + nLength*pOldDataBarInfo->mnZero/100.0);
902     }
903     else
904     {
905         nPosZero = aPaintRect.Left();
906     }
907 
908     if(pOldDataBarInfo->mnLength < 0)
909     {
910         aPaintRect.SetRight( nPosZero );
911         tools::Long nLength = nPosZero - aPaintRect.Left();
912         aPaintRect.SetLeft( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
913     }
914     else if(pOldDataBarInfo->mnLength > 0)
915     {
916         aPaintRect.SetLeft( nPosZero );
917         tools::Long nLength = aPaintRect.Right() - nPosZero;
918         aPaintRect.SetRight( nPosZero + static_cast<tools::Long>(nLength * pOldDataBarInfo->mnLength/100.0) );
919     }
920     else
921         return;
922 
923     if(pOldDataBarInfo->mbGradient)
924     {
925         rRenderContext.SetLineColor(pOldDataBarInfo->maColor);
926         Gradient aGradient(css::awt::GradientStyle_LINEAR, pOldDataBarInfo->maColor, COL_TRANSPARENT);
927         aGradient.SetSteps(255);
928 
929         if(pOldDataBarInfo->mnLength < 0)
930             aGradient.SetAngle(2700_deg10);
931         else
932             aGradient.SetAngle(900_deg10);
933 
934         rRenderContext.DrawGradient(aPaintRect, aGradient);
935 
936         rRenderContext.SetLineColor();
937     }
938     else
939     {
940         rRenderContext.SetFillColor(pOldDataBarInfo->maColor);
941         rRenderContext.DrawRect(aPaintRect);
942     }
943 
944     //draw axis
945     if(!(pOldDataBarInfo->mnZero && pOldDataBarInfo->mnZero != 100))
946         return;
947 
948     Point aPoint1(nPosZero, rRect.Top());
949     Point aPoint2(nPosZero, rRect.Bottom());
950     LineInfo aLineInfo(LineStyle::Dash, 1);
951     aLineInfo.SetDashCount( 4 );
952     aLineInfo.SetDistance( 3 );
953     aLineInfo.SetDashLen( 3 );
954     rRenderContext.SetFillColor(pOldDataBarInfo->maAxisColor);
955     rRenderContext.SetLineColor(pOldDataBarInfo->maAxisColor);
956     rRenderContext.DrawLine(aPoint1, aPoint2, aLineInfo);
957     rRenderContext.SetLineColor();
958     rRenderContext.SetFillColor();
959 }
960 
getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap,ScIconSetType eType,sal_Int32 nIndex)961 const BitmapEx& getIcon(sc::IconSetBitmapMap & rIconSetBitmapMap, ScIconSetType eType, sal_Int32 nIndex)
962 {
963     return ScIconSetFormat::getBitmap(rIconSetBitmapMap, eType, nIndex);
964 }
965 
drawIconSets(vcl::RenderContext & rRenderContext,const ScIconSetInfo * pOldIconSetInfo,const tools::Rectangle & rRect,tools::Long nOneX,tools::Long nOneY,sc::IconSetBitmapMap & rIconSetBitmapMap)966 void drawIconSets(vcl::RenderContext& rRenderContext, const ScIconSetInfo* pOldIconSetInfo, const tools::Rectangle& rRect, tools::Long nOneX, tools::Long nOneY,
967         sc::IconSetBitmapMap & rIconSetBitmapMap)
968 {
969     ScIconSetType eType = pOldIconSetInfo->eIconSetType;
970     sal_Int32 nIndex = pOldIconSetInfo->nIconIndex;
971     const BitmapEx& rIcon = getIcon(rIconSetBitmapMap, eType, nIndex);
972 
973     tools::Long aHeight = o3tl::convert(10, o3tl::Length::pt, o3tl::Length::mm100);
974 
975     if (pOldIconSetInfo->mnHeight)
976     {
977         if (comphelper::LibreOfficeKit::isActive())
978         {
979             aHeight = rRenderContext.LogicToPixel(Size(0, pOldIconSetInfo->mnHeight), MapMode(MapUnit::MapTwip)).Height();
980             aHeight *= comphelper::LibreOfficeKit::getDPIScale();
981         }
982         else
983         {
984             aHeight = o3tl::convert(pOldIconSetInfo->mnHeight, o3tl::Length::twip, o3tl::Length::mm100);
985         }
986     }
987 
988     Size aSize = rIcon.GetSizePixel();
989     double fRatio = static_cast<double>(aSize.Width()) / aSize.Height();
990     tools::Long aWidth = fRatio * aHeight;
991 
992     rRenderContext.Push();
993     rRenderContext.SetClipRegion(vcl::Region(rRect));
994     rRenderContext.DrawBitmapEx(Point(rRect.Left() + 2 * nOneX, rRect.Bottom() - 2 * nOneY - aHeight), Size(aWidth, aHeight), rIcon);
995     rRenderContext.Pop();
996 }
997 
drawCells(vcl::RenderContext & rRenderContext,std::optional<Color> const & pColor,const SvxBrushItem * pBackground,std::optional<Color> & pOldColor,const SvxBrushItem * & pOldBackground,tools::Rectangle & rRect,tools::Long nPosX,tools::Long nLayoutSign,tools::Long nOneX,tools::Long nOneY,const ScDataBarInfo * pDataBarInfo,const ScDataBarInfo * & pOldDataBarInfo,const ScIconSetInfo * pIconSetInfo,const ScIconSetInfo * & pOldIconSetInfo,sc::IconSetBitmapMap & rIconSetBitmapMap)998 void drawCells(vcl::RenderContext& rRenderContext, std::optional<Color> const & pColor, const SvxBrushItem* pBackground, std::optional<Color>& pOldColor, const SvxBrushItem*& pOldBackground,
999         tools::Rectangle& rRect, tools::Long nPosX, tools::Long nLayoutSign, tools::Long nOneX, tools::Long nOneY, const ScDataBarInfo* pDataBarInfo, const ScDataBarInfo*& pOldDataBarInfo,
1000         const ScIconSetInfo* pIconSetInfo, const ScIconSetInfo*& pOldIconSetInfo,
1001         sc::IconSetBitmapMap & rIconSetBitmapMap)
1002 {
1003     tools::Long nSignedOneX = nOneX * nLayoutSign;
1004     // need to paint if old color scale has been used and now
1005     // we have a different color or a style based background
1006     // we can here fall back to pointer comparison
1007     if (pOldColor && (pBackground || pOldColor != pColor || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo))
1008     {
1009         rRect.SetRight( nPosX-nSignedOneX );
1010         if( !pOldColor->IsTransparent() )
1011         {
1012             rRenderContext.SetFillColor( *pOldColor );
1013             rRenderContext.DrawRect( rRect );
1014         }
1015         if( pOldDataBarInfo )
1016             drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
1017         if( pOldIconSetInfo )
1018             drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
1019 
1020         rRect.SetLeft( nPosX - nSignedOneX );
1021     }
1022 
1023     if ( pOldBackground && (pColor || !SfxPoolItem::areSame(pBackground, pOldBackground) || pOldDataBarInfo || pDataBarInfo || pIconSetInfo || pOldIconSetInfo) )
1024     {
1025         rRect.SetRight( nPosX-nSignedOneX );
1026         if (pOldBackground)             // ==0 if hidden
1027         {
1028             Color aBackCol = pOldBackground->GetColor();
1029             if ( !aBackCol.IsTransparent() )      //! partial transparency?
1030             {
1031                 rRenderContext.SetFillColor( aBackCol );
1032                 rRenderContext.DrawRect( rRect );
1033             }
1034         }
1035         if( pOldDataBarInfo )
1036             drawDataBars(rRenderContext, pOldDataBarInfo, rRect, nOneX, nOneY);
1037         if( pOldIconSetInfo )
1038             drawIconSets(rRenderContext, pOldIconSetInfo, rRect, nOneX, nOneY, rIconSetBitmapMap);
1039 
1040         rRect.SetLeft( nPosX - nSignedOneX );
1041     }
1042 
1043     if (!pOldBackground && !pOldColor && (pDataBarInfo || pIconSetInfo))
1044     {
1045         rRect.SetRight( nPosX -nSignedOneX );
1046         rRect.SetLeft( nPosX - nSignedOneX );
1047     }
1048 
1049     if(pColor)
1050     {
1051         // only update pOldColor if the colors changed
1052         if (!pOldColor || *pOldColor != *pColor)
1053             pOldColor = pColor;
1054 
1055         pOldBackground = nullptr;
1056     }
1057     else if(pBackground)
1058     {
1059         pOldBackground = pBackground;
1060         pOldColor.reset();
1061     }
1062 
1063     if(pDataBarInfo)
1064         pOldDataBarInfo = pDataBarInfo;
1065     else
1066         pOldDataBarInfo = nullptr;
1067 
1068     if(pIconSetInfo)
1069         pOldIconSetInfo = pIconSetInfo;
1070     else
1071         pOldIconSetInfo = nullptr;
1072 }
1073 
1074 }
1075 
DrawBackground(vcl::RenderContext & rRenderContext)1076 void ScOutputData::DrawBackground(vcl::RenderContext& rRenderContext)
1077 {
1078     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1079     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1080 
1081     Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1082     tools::Long nOneXLogic = aOnePixel.Width();
1083     tools::Long nOneYLogic = aOnePixel.Height();
1084 
1085     // See more about bWorksInPixels in ScOutputData::DrawGrid
1086     bool bWorksInPixels = false;
1087     if (eType == OUTTYPE_WINDOW)
1088         bWorksInPixels = true;
1089 
1090     tools::Long nOneX = 1;
1091     tools::Long nOneY = 1;
1092     if (!bWorksInPixels)
1093     {
1094         nOneX = nOneXLogic;
1095         nOneY = nOneYLogic;
1096     }
1097 
1098     tools::Rectangle aRect;
1099 
1100     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1101 
1102     rRenderContext.SetLineColor();
1103 
1104     bool bShowProt = mbSyntaxMode && mpDoc->IsTabProtected(nTab);
1105     bool bDoAll = bShowProt || bPagebreakMode || bSolidBackground;
1106 
1107     bool bCellContrast = mbUseStyleColor &&
1108             Application::GetSettings().GetStyleSettings().GetHighContrastMode();
1109 
1110     tools::Long nPosY = nScrY;
1111 
1112     const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1113     Color aProtectedColor( rColorCfg.GetColorValue( svtools::CALCPROTECTEDBACKGROUND ).nColor );
1114     auto pProtectedBackground = std::make_shared<SvxBrushItem>( aProtectedColor, ATTR_BACKGROUND );
1115 
1116     // iterate through the rows to show
1117     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
1118     {
1119         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1120         tools::Long nRowHeight = pThisRowInfo->nHeight;
1121 
1122         if ( pThisRowInfo->bChanged )
1123         {
1124             if ( ( ( pThisRowInfo->bEmptyBack ) || mbSyntaxMode ) && !bDoAll )
1125             {
1126                 // nothing
1127             }
1128             else
1129             {
1130                 if (bTaggedPDF)
1131                     pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
1132 
1133                 // scan for rows with the same background:
1134                 SCSIZE nSkip = 0;
1135                 while ( nArrY+nSkip+2<nArrCount &&
1136                         lcl_EqualBack( *pThisRowInfo, pRowInfo[nArrY+nSkip+1],
1137                                         nX1, nX2, bShowProt, bPagebreakMode ) )
1138                 {
1139                     ++nSkip;
1140                     nRowHeight += pRowInfo[nArrY+nSkip].nHeight;    // after incrementing
1141                 }
1142 
1143                 tools::Long nPosX = nScrX;
1144 
1145                 if ( bLayoutRTL )
1146                     nPosX += nMirrorW - nOneX;
1147 
1148                 aRect = tools::Rectangle(nPosX, nPosY - nOneY, nPosX, nPosY - nOneY + nRowHeight);
1149                 if (bWorksInPixels)
1150                     aRect = rRenderContext.PixelToLogic(aRect); // internal data in pixels, but we'll be drawing in logic units
1151 
1152                 const SvxBrushItem* pOldBackground = nullptr;
1153                 const SvxBrushItem* pBackground = nullptr;
1154                 std::optional<Color> pOldColor;
1155                 const ScDataBarInfo* pOldDataBarInfo = nullptr;
1156                 const ScIconSetInfo* pOldIconSetInfo = nullptr;
1157                 SCCOL nMergedCols = 1;
1158                 SCCOL nOldMerged = 0;
1159 
1160                 for (SCCOL nX=nX1; nX + nMergedCols <= nX2 + 1; nX += nOldMerged)
1161                 {
1162                     ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX-1+nMergedCols);
1163 
1164                     nOldMerged = nMergedCols;
1165 
1166                     tools::Long nNewPosX = nPosX;
1167                     // extend for all merged cells
1168                     nMergedCols = 1;
1169                     if (pInfo->bMerged && pInfo->pPatternAttr)
1170                     {
1171                             const ScMergeAttr* pMerge =
1172                                     &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
1173                             nMergedCols = std::max<SCCOL>(1, pMerge->GetColMerge());
1174                     }
1175 
1176                     for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
1177                     {
1178                         SCCOL nCol = nX+nOldMerged+nMerged;
1179                         if (nCol > nX2+2)
1180                             break;
1181                         nNewPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * nLayoutSign;
1182                     }
1183 
1184                     if (nNewPosX == nPosX)
1185                         continue; // Zero width, no need to draw.
1186 
1187                     if (bCellContrast)
1188                     {
1189                         //  high contrast for cell borders and backgrounds -> empty background
1190                         pBackground = ScGlobal::GetEmptyBrushItem();
1191                     }
1192                     else if (bShowProt)         // show cell protection in syntax mode
1193                     {
1194                         const ScPatternAttr* pP = pInfo->pPatternAttr;
1195                         if (pP)
1196                         {
1197                             const ScProtectionAttr& rProt = pP->GetItem(ATTR_PROTECTION);
1198                             if (rProt.GetProtection() || rProt.GetHideCell())
1199                                 pBackground = pProtectedBackground.get();
1200                             else
1201                                 pBackground = ScGlobal::GetEmptyBrushItem();
1202                         }
1203                         else
1204                             pBackground = nullptr;
1205                     }
1206                     else
1207                         pBackground = static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem());
1208 
1209                     if ( bPagebreakMode && !pInfo->bPrinted )
1210                         pBackground = pProtectedBackground.get();
1211 
1212                     if ( pInfo->nRotateDir > ScRotateDir::Standard &&
1213                             !pBackground->GetColor().IsFullyTransparent() &&
1214                             !bCellContrast )
1215                     {
1216                         SCROW nY = pRowInfo[nArrY].nRowNo;
1217                         pBackground = lcl_FindBackground( mpDoc, nX, nY, nTab );
1218                     }
1219 
1220                     std::optional<Color> const & pColor = pInfo->mxColorScale;
1221                     const ScDataBarInfo* pDataBarInfo = pInfo->pDataBar;
1222                     const ScIconSetInfo* pIconSetInfo = pInfo->pIconSet;
1223 
1224                     tools::Long nPosXLogic = nPosX;
1225                     if (bWorksInPixels)
1226                         nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
1227 
1228                     drawCells(rRenderContext, pColor, pBackground, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
1229 
1230                     nPosX = nNewPosX;
1231                 }
1232 
1233                 tools::Long nPosXLogic = nPosX;
1234                 if (bWorksInPixels)
1235                     nPosXLogic = rRenderContext.PixelToLogic(Point(nPosX, 0)).X();
1236 
1237                 drawCells(rRenderContext, std::optional<Color>(), nullptr, pOldColor, pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, nullptr, pOldDataBarInfo, nullptr, pOldIconSetInfo, mpDoc->GetIconSetBitmapMap());
1238 
1239                 nArrY += nSkip;
1240 
1241                 if (bTaggedPDF)
1242                     pPDF->EndStructureElement();
1243             }
1244         }
1245         nPosY += nRowHeight;
1246     }
1247 }
1248 
DrawShadow()1249 void ScOutputData::DrawShadow()
1250 {
1251     DrawExtraShadow( false, false, false, false );
1252 }
1253 
DrawExtraShadow(bool bLeft,bool bTop,bool bRight,bool bBottom)1254 void ScOutputData::DrawExtraShadow(bool bLeft, bool bTop, bool bRight, bool bBottom)
1255 {
1256     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1257     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1258 
1259     mpDev->SetLineColor();
1260 
1261     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1262     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1263     Color aAutoTextColor;
1264     if ( bCellContrast )
1265         aAutoTextColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
1266 
1267     tools::Long nInitPosX = nScrX;
1268     if ( bLayoutRTL )
1269     {
1270         Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
1271         tools::Long nOneX = aOnePixel.Width();
1272         nInitPosX += nMirrorW - nOneX;
1273     }
1274     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1275 
1276     tools::Long nPosY = nScrY - pRowInfo[0].nHeight;
1277     for (SCSIZE nArrY=0; nArrY<nArrCount; nArrY++)
1278     {
1279         bool bCornerY = ( nArrY == 0 ) || ( nArrY+1 == nArrCount );
1280         bool bSkipY = ( nArrY==0 && !bTop ) || ( nArrY+1 == nArrCount && !bBottom );
1281 
1282         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1283         tools::Long nRowHeight = pThisRowInfo->nHeight;
1284 
1285         if ( pThisRowInfo->bChanged && !bSkipY )
1286         {
1287             tools::Long nPosX = nInitPosX - pRowInfo[0].basicCellInfo(nX1-1).nWidth * nLayoutSign;
1288             for (SCCOL nCol=nX1-1; nCol<=nX2+1; nCol++)
1289             {
1290                 bool bCornerX = ( nCol==nX1-1 || nCol==nX2+1 );
1291                 bool bSkipX = ( nCol==nX1-1 && !bLeft ) || ( nCol==nX2+1 && !bRight );
1292 
1293                 for (sal_uInt16 nPass=0; nPass<2; nPass++) // horizontal / vertical
1294                 {
1295                     const SvxShadowItem* pAttr = nPass ?
1296                             pThisRowInfo->cellInfo(nCol).pVShadowOrigin :
1297                             pThisRowInfo->cellInfo(nCol).pHShadowOrigin;
1298                     if ( pAttr && !bSkipX )
1299                     {
1300                         if (bTaggedPDF)
1301                             pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
1302 
1303                         ScShadowPart ePart = nPass ?
1304                                 pThisRowInfo->cellInfo(nCol).eVShadowPart :
1305                                 pThisRowInfo->cellInfo(nCol).eHShadowPart;
1306 
1307                         bool bDo = true;
1308                         if ( (nPass==0 && bCornerX) || (nPass==1 && bCornerY) )
1309                             if ( ePart != SC_SHADOW_CORNER )
1310                                 bDo = false;
1311 
1312                         if (bDo)
1313                         {
1314                             tools::Long nThisWidth = pRowInfo[0].basicCellInfo(nCol).nWidth;
1315                             tools::Long nMaxWidth = nThisWidth;
1316                             if (!nMaxWidth)
1317                             {
1318                                 //! direction must depend on shadow location
1319                                 SCCOL nWx = nCol+1;
1320                                 while (nWx<nX2 && !pRowInfo[0].basicCellInfo(nWx).nWidth)
1321                                     ++nWx;
1322                                 nMaxWidth = pRowInfo[0].basicCellInfo(nWx).nWidth;
1323                             }
1324 
1325                             // rectangle is in logical orientation
1326                             tools::Rectangle aRect( nPosX, nPosY,
1327                                              nPosX + ( nThisWidth - 1 ) * nLayoutSign,
1328                                              nPosY + pRowInfo[nArrY].nHeight - 1 );
1329 
1330                             tools::Long nSize = pAttr->GetWidth();
1331                             tools::Long nSizeX = static_cast<tools::Long>(nSize*mnPPTX);
1332                             if (nSizeX >= nMaxWidth) nSizeX = nMaxWidth-1;
1333                             tools::Long nSizeY = static_cast<tools::Long>(nSize*mnPPTY);
1334                             if (nSizeY >= nRowHeight) nSizeY = nRowHeight-1;
1335 
1336                             nSizeX *= nLayoutSign;      // used only to add to rectangle values
1337 
1338                             SvxShadowLocation eLoc = pAttr->GetLocation();
1339                             if ( bLayoutRTL )
1340                             {
1341                                 //  Shadow location is specified as "visual" (right is always right),
1342                                 //  so the attribute's location value is mirrored here and in FillInfo.
1343                                 switch (eLoc)
1344                                 {
1345                                     case SvxShadowLocation::BottomRight: eLoc = SvxShadowLocation::BottomLeft;  break;
1346                                     case SvxShadowLocation::BottomLeft:  eLoc = SvxShadowLocation::BottomRight; break;
1347                                     case SvxShadowLocation::TopRight:    eLoc = SvxShadowLocation::TopLeft;     break;
1348                                     case SvxShadowLocation::TopLeft:     eLoc = SvxShadowLocation::TopRight;    break;
1349                                     default:
1350                                     {
1351                                         // added to avoid warnings
1352                                     }
1353                                 }
1354                             }
1355 
1356                             if (ePart == SC_SHADOW_HORIZ || ePart == SC_SHADOW_HSTART ||
1357                                 ePart == SC_SHADOW_CORNER)
1358                             {
1359                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
1360                                     aRect.SetTop( aRect.Bottom() - nSizeY );
1361                                 else
1362                                     aRect.SetBottom( aRect.Top() + nSizeY );
1363                             }
1364                             if (ePart == SC_SHADOW_VERT || ePart == SC_SHADOW_VSTART ||
1365                                 ePart == SC_SHADOW_CORNER)
1366                             {
1367                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
1368                                     aRect.SetLeft( aRect.Right() - nSizeX );
1369                                 else
1370                                     aRect.SetRight( aRect.Left() + nSizeX );
1371                             }
1372                             if (ePart == SC_SHADOW_HSTART)
1373                             {
1374                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::BottomLeft)
1375                                     aRect.AdjustRight( -nSizeX );
1376                                 else
1377                                     aRect.AdjustLeft(nSizeX );
1378                             }
1379                             if (ePart == SC_SHADOW_VSTART)
1380                             {
1381                                 if (eLoc == SvxShadowLocation::TopLeft || eLoc == SvxShadowLocation::TopRight)
1382                                     aRect.AdjustBottom( -nSizeY );
1383                                 else
1384                                     aRect.AdjustTop(nSizeY );
1385                             }
1386 
1387                             //! merge rectangles?
1388                             mpDev->SetFillColor( bCellContrast ? aAutoTextColor : pAttr->GetColor() );
1389                             mpDev->DrawRect( aRect );
1390 
1391                             if (bTaggedPDF)
1392                                 pPDF->EndStructureElement();
1393                         }
1394                     }
1395                 }
1396 
1397                 nPosX += pRowInfo[0].basicCellInfo(nCol).nWidth * nLayoutSign;
1398             }
1399         }
1400         nPosY += nRowHeight;
1401     }
1402 }
1403 
DrawClear()1404 void ScOutputData::DrawClear()
1405 {
1406     tools::Rectangle aRect;
1407     Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
1408     tools::Long nOneX = aOnePixel.Width();
1409     tools::Long nOneY = aOnePixel.Height();
1410 
1411     // (called only for ScGridWindow)
1412     Color aBgColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
1413 
1414     if (bMetaFile)
1415         nOneX = nOneY = 0;
1416 
1417     mpDev->SetLineColor();
1418 
1419     mpDev->SetFillColor( aBgColor );
1420 
1421     tools::Long nPosY = nScrY;
1422     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
1423     {
1424         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1425         tools::Long nRowHeight = pThisRowInfo->nHeight;
1426 
1427         if ( pThisRowInfo->bChanged )
1428         {
1429             // scan for more rows which must be painted:
1430             SCSIZE nSkip = 0;
1431             while ( nArrY+nSkip+2<nArrCount && pRowInfo[nArrY+nSkip+1].bChanged )
1432             {
1433                 ++nSkip;
1434                 nRowHeight += pRowInfo[nArrY+nSkip].nHeight;    // after incrementing
1435             }
1436 
1437             aRect = tools::Rectangle( Point( nScrX, nPosY ),
1438                     Size( nScrW+1-nOneX, nRowHeight+1-nOneY) );
1439             mpDev->DrawRect( aRect );
1440 
1441             nArrY += nSkip;
1442         }
1443         nPosY += nRowHeight;
1444     }
1445 }
1446 
1447 // Lines
1448 
lclGetSnappedX(const OutputDevice & rDev,tools::Long nPosX,bool bSnapPixel)1449 static tools::Long lclGetSnappedX( const OutputDevice& rDev, tools::Long nPosX, bool bSnapPixel )
1450 {
1451     return (bSnapPixel && nPosX) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( nPosX, 0 ) ) ).Width() : nPosX;
1452 }
1453 
lclGetSnappedY(const OutputDevice & rDev,tools::Long nPosY,bool bSnapPixel)1454 static tools::Long lclGetSnappedY( const OutputDevice& rDev, tools::Long nPosY, bool bSnapPixel )
1455 {
1456     return (bSnapPixel && nPosY) ? rDev.PixelToLogic( rDev.LogicToPixel( Size( 0, nPosY ) ) ).Height() : nPosY;
1457 }
1458 
DrawFrame(vcl::RenderContext & rRenderContext)1459 void ScOutputData::DrawFrame(vcl::RenderContext& rRenderContext)
1460 {
1461     vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(mpDev->GetExtOutDevData());
1462     bool bTaggedPDF = pPDF && pPDF->GetIsExportTaggedPDF();
1463     if (bTaggedPDF)
1464         pPDF->WrapBeginStructureElement(vcl::PDFWriter::NonStructElement);
1465 
1466     DrawModeFlags nOldDrawMode = rRenderContext.GetDrawMode();
1467 
1468     Color aSingleColor;
1469     bool bUseSingleColor = false;
1470     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1471     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1472 
1473     //  if a Calc OLE object is embedded in Draw/Impress, the VCL DrawMode is used
1474     //  for display mode / B&W printing. The VCL DrawMode handling doesn't work for lines
1475     //  that are drawn with DrawRect, so if the line/background bits are set, the DrawMode
1476     //  must be reset and the border colors handled here.
1477 
1478     if ( ( nOldDrawMode & DrawModeFlags::WhiteFill ) && ( nOldDrawMode & DrawModeFlags::BlackLine ) )
1479     {
1480         rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::WhiteFill) );
1481         aSingleColor = COL_BLACK;
1482         bUseSingleColor = true;
1483     }
1484     else if ( ( nOldDrawMode & DrawModeFlags::SettingsFill ) && ( nOldDrawMode & DrawModeFlags::SettingsLine ) )
1485     {
1486         rRenderContext.SetDrawMode( nOldDrawMode & (~DrawModeFlags::SettingsFill) );
1487         aSingleColor = rStyleSettings.GetWindowTextColor();     // same as used in VCL for DrawModeFlags::SettingsLine
1488         bUseSingleColor = true;
1489     }
1490     else if ( bCellContrast )
1491     {
1492         aSingleColor = SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
1493         bUseSingleColor = true;
1494     }
1495 
1496     const Color* pForceColor = bUseSingleColor ? &aSingleColor : nullptr;
1497 
1498     if (mrTabInfo.maArray.HasCellRotation())
1499     {
1500         DrawRotatedFrame(rRenderContext);        // removes the lines that must not be painted here
1501     }
1502 
1503     tools::Long nInitPosX = nScrX;
1504     if ( bLayoutRTL )
1505     {
1506         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1507         tools::Long nOneX = aOnePixel.Width();
1508         nInitPosX += nMirrorW - nOneX;
1509     }
1510     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1511 
1512     // *** set column and row sizes of the frame border array ***
1513 
1514     svx::frame::Array& rArray = mrTabInfo.maArray;
1515     size_t nColCount = rArray.GetColCount();
1516     size_t nRowCount = rArray.GetRowCount();
1517 
1518     // row heights
1519 
1520     // row 0 is not visible (dummy for borders from top) - subtract its height from initial position
1521     // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit before
1522     tools::Long nOldPosY = nScrY - 1 - pRowInfo[ 0 ].nHeight;
1523     tools::Long nOldSnapY = lclGetSnappedY( rRenderContext, nOldPosY, bSnapPixel );
1524     rArray.SetYOffset( nOldSnapY );
1525     for( size_t nRow = 0; nRow < nRowCount; ++nRow )
1526     {
1527         tools::Long nNewPosY = nOldPosY + pRowInfo[ nRow ].nHeight;
1528         tools::Long nNewSnapY = lclGetSnappedY( rRenderContext, nNewPosY, bSnapPixel );
1529         rArray.SetRowHeight( nRow, nNewSnapY - nOldSnapY );
1530         nOldPosY = nNewPosY;
1531         nOldSnapY = nNewSnapY;
1532     }
1533 
1534     // column widths
1535 
1536     // column nX1-1 is not visible (dummy for borders from left) - subtract its width from initial position
1537     // subtract 1 unit more, because position 0 is first *in* cell, grid line is one unit above
1538     tools::Long nOldPosX = nInitPosX - nLayoutSign * (1 + pRowInfo[ 0 ].basicCellInfo( nX1 - 1 ).nWidth);
1539     tools::Long nOldSnapX = lclGetSnappedX( rRenderContext, nOldPosX, bSnapPixel );
1540     // set X offset for left-to-right sheets; for right-to-left sheets this is done after for() loop
1541     if( !bLayoutRTL )
1542         rArray.SetXOffset( nOldSnapX );
1543     for( SCCOL nCol = nX1 - 1; nCol <= nX2 + 1; ++nCol )
1544     {
1545         size_t nArrCol = bLayoutRTL ? nX2 + 1 - nCol : nCol - (nX1 - 1);
1546         tools::Long nNewPosX = nOldPosX + pRowInfo[ 0 ].basicCellInfo( nCol ).nWidth * nLayoutSign;
1547         tools::Long nNewSnapX = lclGetSnappedX( rRenderContext, nNewPosX, bSnapPixel );
1548         rArray.SetColWidth( nArrCol, std::abs( nNewSnapX - nOldSnapX ) );
1549         nOldPosX = nNewPosX;
1550         nOldSnapX = nNewSnapX;
1551     }
1552     if( bLayoutRTL )
1553         rArray.SetXOffset( nOldSnapX );
1554 
1555     // *** draw the array ***
1556 
1557     size_t nFirstCol = 1;
1558     size_t nFirstRow = 1;
1559     size_t nLastCol = nColCount - 2;
1560     size_t nLastRow = nRowCount - 2;
1561 
1562     if( mrTabInfo.mbPageMode )
1563         rArray.SetClipRange( nFirstCol, nFirstRow, nLastCol, nLastRow );
1564 
1565     // draw only rows with set RowInfo::bChanged flag
1566     size_t nRow1 = nFirstRow;
1567     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D());
1568     if (!pProcessor)
1569         return;
1570     while( nRow1 <= nLastRow )
1571     {
1572         while( (nRow1 <= nLastRow) && !pRowInfo[ nRow1 ].bChanged ) ++nRow1;
1573         if( nRow1 <= nLastRow )
1574         {
1575             size_t nRow2 = nRow1;
1576             while( (nRow2 + 1 <= nLastRow) && pRowInfo[ nRow2 + 1 ].bChanged ) ++nRow2;
1577             auto xPrimitive = rArray.CreateB2DPrimitiveRange(
1578                     nFirstCol, nRow1, nLastCol, nRow2, pForceColor );
1579             pProcessor->process(xPrimitive);
1580             nRow1 = nRow2 + 1;
1581         }
1582     }
1583     pProcessor.reset();
1584 
1585     rRenderContext.SetDrawMode(nOldDrawMode);
1586 
1587     if (bTaggedPDF)
1588         pPDF->EndStructureElement();
1589 }
1590 
DrawRotatedFrame(vcl::RenderContext & rRenderContext)1591 void ScOutputData::DrawRotatedFrame(vcl::RenderContext& rRenderContext)
1592 {
1593     //! save nRotMax
1594     SCCOL nRotMax = nX2;
1595     for (SCSIZE nRotY=0; nRotY<nArrCount; nRotY++)
1596         if (pRowInfo[nRotY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nRotY].nRotMaxCol > nRotMax)
1597             nRotMax = pRowInfo[nRotY].nRotMaxCol;
1598 
1599     const ScPatternAttr* pPattern;
1600     const SfxItemSet*    pCondSet;
1601 
1602     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1603     bool bCellContrast = mbUseStyleColor && rStyleSettings.GetHighContrastMode();
1604 
1605     tools::Long nInitPosX = nScrX;
1606     if ( bLayoutRTL )
1607     {
1608         Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
1609         tools::Long nOneX = aOnePixel.Width();
1610         nInitPosX += nMirrorW - nOneX;
1611     }
1612     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
1613 
1614     tools::Rectangle aClipRect( Point(nScrX, nScrY), Size(nScrW, nScrH) );
1615     if (bMetaFile)
1616     {
1617         rRenderContext.Push();
1618         rRenderContext.IntersectClipRegion( aClipRect );
1619     }
1620     else
1621         rRenderContext.SetClipRegion( vcl::Region( aClipRect ) );
1622 
1623     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(CreateProcessor2D( ));
1624     tools::Long nPosY = nScrY;
1625     for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)
1626     {
1627         // Rotated is also drawn one line above/below Changed if parts extend into the cell
1628 
1629         RowInfo& rPrevRowInfo = pRowInfo[nArrY-1];
1630         RowInfo& rThisRowInfo = pRowInfo[nArrY];
1631         RowInfo& rNextRowInfo = pRowInfo[nArrY+1];
1632 
1633         tools::Long nRowHeight = rThisRowInfo.nHeight;
1634         if ( rThisRowInfo.nRotMaxCol != SC_ROTMAX_NONE &&
1635              ( rThisRowInfo.bChanged || rPrevRowInfo.bChanged ||
1636                ( nArrY+1<nArrCount && rNextRowInfo.bChanged ) ) )
1637         {
1638             SCROW nY = rThisRowInfo.nRowNo;
1639             tools::Long nPosX = 0;
1640             SCCOL nX;
1641             for (nX=0; nX<=nRotMax; nX++)
1642             {
1643                 if (nX==nX1) nPosX = nInitPosX;     // calculated individually for preceding positions
1644 
1645                 ScCellInfo* pInfo = &rThisRowInfo.cellInfo(nX);
1646                 tools::Long nColWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
1647                 if ( pInfo->nRotateDir > ScRotateDir::Standard &&
1648                         !pInfo->bHOverlapped && !pInfo->bVOverlapped )
1649                 {
1650                     pPattern = pInfo->pPatternAttr;
1651                     pCondSet = pInfo->pConditionSet;
1652                     if (!pPattern)
1653                     {
1654                         pPattern = mpDoc->GetPattern( nX, nY, nTab );
1655                         pInfo->pPatternAttr = pPattern;
1656                         pCondSet = mpDoc->GetCondResult( nX, nY, nTab );
1657                         pInfo->pConditionSet = pCondSet;
1658                     }
1659 
1660                     //! LastPattern etc.
1661 
1662                     Degree100 nAttrRotate = pPattern->GetRotateVal( pCondSet );
1663                     SvxRotateMode eRotMode =
1664                                     pPattern->GetItem(ATTR_ROTATE_MODE, pCondSet).GetValue();
1665 
1666                     if (nAttrRotate)
1667                     {
1668                         if (nX < nX1)         // compute negative position
1669                         {
1670                             nPosX = nInitPosX;
1671                             SCCOL nCol = nX1;
1672                             while (nCol > nX)
1673                             {
1674                                 --nCol;
1675                                 nPosX -= nLayoutSign * static_cast<tools::Long>(pRowInfo[0].basicCellInfo(nCol).nWidth);
1676                             }
1677                         }
1678 
1679                         // start position minus 1 so rotated backgrounds suit the border
1680                         // (border is on the grid)
1681 
1682                         tools::Long nTop = nPosY - 1;
1683                         tools::Long nBottom = nPosY + nRowHeight - 1;
1684                         tools::Long nTopLeft = nPosX - nLayoutSign;
1685                         tools::Long nTopRight = nPosX + (nColWidth - 1) * nLayoutSign;
1686                         tools::Long nBotLeft = nTopLeft;
1687                         tools::Long nBotRight = nTopRight;
1688 
1689                         // inclusion of the sign here hasn't been decided yet
1690                         // (if not, the extension of the non-rotated background must also be changed)
1691                         double nRealOrient = nLayoutSign * toRadians(nAttrRotate);     // 1/100th degrees
1692                         double nCos = cos(nRealOrient);
1693                         double nSin = sin(nRealOrient);
1694                         //! restrict !!!
1695                         tools::Long nSkew = static_cast<tools::Long>(nRowHeight * nCos / nSin);
1696 
1697                         switch (eRotMode)
1698                         {
1699                         case SVX_ROTATE_MODE_BOTTOM:
1700                             nTopLeft += nSkew;
1701                             nTopRight += nSkew;
1702                             break;
1703                         case SVX_ROTATE_MODE_CENTER:
1704                             nSkew /= 2;
1705                             nTopLeft += nSkew;
1706                             nTopRight += nSkew;
1707                             nBotLeft -= nSkew;
1708                             nBotRight -= nSkew;
1709                             break;
1710                         case SVX_ROTATE_MODE_TOP:
1711                             nBotLeft -= nSkew;
1712                             nBotRight -= nSkew;
1713                             break;
1714                         default:
1715                         {
1716                             // added to avoid warnings
1717                         }
1718                         }
1719 
1720                         Point aPoints[4];
1721                         aPoints[0] = Point(nTopLeft, nTop);
1722                         aPoints[1] = Point(nTopRight, nTop);
1723                         aPoints[2] = Point(nBotRight, nBottom);
1724                         aPoints[3] = Point(nBotLeft, nBottom);
1725 
1726                         const SvxBrushItem* pBackground(static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem()));
1727                         if (!pBackground)
1728                             pBackground = &pPattern->GetItem(ATTR_BACKGROUND, pCondSet);
1729                         if (bCellContrast)
1730                         {
1731                             //  high contrast for cell borders and backgrounds -> empty background
1732                             pBackground = ScGlobal::GetEmptyBrushItem();
1733                         }
1734                         if (!pInfo->mxColorScale)
1735                         {
1736                             const Color& rColor = pBackground->GetColor();
1737                             if (rColor.GetAlpha() != 0)
1738                             {
1739                                 //  draw background only for the changed row itself
1740                                 //  (background doesn't extend into other cells).
1741                                 //  For the borders (rotated and normal), clipping should be
1742                                 //  set if the row isn't changed, but at least the borders
1743                                 //  don't cover the cell contents.
1744                                 if (rThisRowInfo.bChanged)
1745                                 {
1746                                     tools::Polygon aPoly(4, aPoints);
1747 
1748                                     // for DrawPolygon, without Pen one pixel is left out
1749                                     // to the right and below...
1750                                     if (!rColor.IsTransparent())
1751                                         rRenderContext.SetLineColor(rColor);
1752                                     else
1753                                         rRenderContext.SetLineColor();
1754                                     rRenderContext.SetFillColor(rColor);
1755                                     rRenderContext.DrawPolygon(aPoly);
1756                                 }
1757                             }
1758                         }
1759                         else
1760                         {
1761                             tools::Polygon aPoly(4, aPoints);
1762                             std::optional<Color> const & pColor = pInfo->mxColorScale;
1763 
1764                             // for DrawPolygon, without Pen one pixel is left out
1765                             // to the right and below...
1766                             if (!pColor->IsTransparent())
1767                                 rRenderContext.SetLineColor(*pColor);
1768                             else
1769                                 rRenderContext.SetLineColor();
1770                             rRenderContext.SetFillColor(*pColor);
1771                             rRenderContext.DrawPolygon(aPoly);
1772 
1773                         }
1774                     }
1775                 }
1776                 nPosX += nColWidth * nLayoutSign;
1777             }
1778         }
1779         nPosY += nRowHeight;
1780     }
1781 
1782     pProcessor.reset();
1783 
1784     if (bMetaFile)
1785         rRenderContext.Pop();
1786     else
1787         rRenderContext.SetClipRegion();
1788 }
1789 
CreateProcessor2D()1790 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> ScOutputData::CreateProcessor2D( )
1791 {
1792     mpDoc->InitDrawLayer(mpDoc->GetDocumentShell());
1793     ScDrawLayer* pDrawLayer = mpDoc->GetDrawLayer();
1794     if (!pDrawLayer)
1795         return nullptr;
1796 
1797     basegfx::B2DRange aViewRange;
1798     SdrPage *pDrawPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) );
1799     drawinglayer::geometry::ViewInformation2D aNewViewInfos;
1800     aNewViewInfos.setViewTransformation(mpDev->GetViewTransformation());
1801     aNewViewInfos.setViewport(aViewRange);
1802     aNewViewInfos.setVisualizedPage(GetXDrawPageForSdrPage( pDrawPage ));
1803 
1804     return drawinglayer::processor2d::createProcessor2DFromOutputDevice(
1805                     *mpDev, aNewViewInfos );
1806 }
1807 
1808 // Printer
1809 
GetChangedAreaRegion()1810 vcl::Region ScOutputData::GetChangedAreaRegion()
1811 {
1812     vcl::Region aRegion;
1813     tools::Rectangle aDrawingRect;
1814     bool bHad(false);
1815     tools::Long nPosY = nScrY;
1816     SCSIZE nArrY;
1817 
1818     aDrawingRect.SetLeft( nScrX );
1819     aDrawingRect.SetRight( nScrX+nScrW-1 );
1820 
1821     for(nArrY=1; nArrY+1<nArrCount; nArrY++)
1822     {
1823         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1824 
1825         if(pThisRowInfo->bChanged)
1826         {
1827             if(!bHad)
1828             {
1829                 aDrawingRect.SetTop( nPosY );
1830                 bHad = true;
1831             }
1832 
1833             aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
1834         }
1835         else if(bHad)
1836         {
1837             aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
1838             bHad = false;
1839         }
1840 
1841         nPosY += pRowInfo[nArrY].nHeight;
1842     }
1843 
1844     if(bHad)
1845     {
1846         aRegion.Union(mpDev->PixelToLogic(aDrawingRect));
1847     }
1848 
1849     return aRegion;
1850 }
1851 
SetChangedClip()1852 bool ScOutputData::SetChangedClip()
1853 {
1854     tools::PolyPolygon aPoly;
1855 
1856     tools::Rectangle aDrawingRect;
1857     aDrawingRect.SetLeft( nScrX );
1858     aDrawingRect.SetRight( nScrX+nScrW-1 );
1859 
1860     bool    bHad    = false;
1861     tools::Long    nPosY   = nScrY;
1862     SCSIZE  nArrY;
1863     for (nArrY=1; nArrY+1<nArrCount; nArrY++)
1864     {
1865         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1866 
1867         if ( pThisRowInfo->bChanged )
1868         {
1869             if (!bHad)
1870             {
1871                 aDrawingRect.SetTop( nPosY );
1872                 bHad = true;
1873             }
1874             aDrawingRect.SetBottom( nPosY + pRowInfo[nArrY].nHeight - 1 );
1875         }
1876         else if (bHad)
1877         {
1878             aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
1879             bHad = false;
1880         }
1881         nPosY += pRowInfo[nArrY].nHeight;
1882     }
1883 
1884     if (bHad)
1885         aPoly.Insert( tools::Polygon( mpDev->PixelToLogic(aDrawingRect) ) );
1886 
1887     bool bRet = (aPoly.Count() != 0);
1888     if (bRet)
1889         mpDev->SetClipRegion(vcl::Region(aPoly));
1890     return bRet;
1891 }
1892 
FindChanged()1893 void ScOutputData::FindChanged()
1894 {
1895     SCCOL   nX;
1896     SCSIZE  nArrY;
1897 
1898     bool bWasIdleEnabled = mpDoc->IsIdleEnabled();
1899     mpDoc->EnableIdle(false);
1900     for (nArrY=0; nArrY<nArrCount; nArrY++)
1901         pRowInfo[nArrY].bChanged = false;
1902 
1903     SCCOL nCol1 = mpDoc->MaxCol(), nCol2 = 0;
1904     SCROW nRow1 = mpDoc->MaxRow(), nRow2 = 0;
1905     bool bAnyDirty = false;
1906     bool bAnyChanged = false;
1907 
1908     for (nArrY=0; nArrY<nArrCount; nArrY++)
1909     {
1910         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1911         for (nX=nX1; nX<=nX2; nX++)
1912         {
1913             const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
1914 
1915             if (rCell.getType() != CELLTYPE_FORMULA)
1916                 continue;
1917 
1918             ScFormulaCell* pFCell = rCell.getFormula();
1919             if (pFCell->IsRunning())
1920                 // still being interpreted. Skip it.
1921                 continue;
1922 
1923             bool bDirty = pFCell->GetDirty();
1924             bAnyChanged = bAnyChanged || pFCell->IsChanged();
1925 
1926             if (bDirty)
1927             {
1928                 if (!bAnyDirty)
1929                 {
1930                     ScProgress::CreateInterpretProgress(mpDoc);
1931                     bAnyDirty = true;
1932                 }
1933 
1934                 ScAddress& rPos(pFCell->aPos);
1935                 nCol1 = std::min(rPos.Col(), nCol1);
1936                 nCol2 = std::max(rPos.Col(), nCol2);
1937                 nRow1 = std::min(rPos.Row(), nRow1);
1938                 nRow2 = std::max(rPos.Row(), nRow2);
1939 
1940                 const SfxUInt32Item* pItem = mpDoc->GetAttr(rPos, ATTR_VALIDDATA);
1941                 const ScValidationData* pData = mpDoc->GetValidationEntry(pItem->GetValue());
1942                 if (pData)
1943                 {
1944                     ScRefCellValue aCell(*mpDoc, rPos);
1945                     if (pData->IsDataValid(aCell, rPos))
1946                         ScDetectiveFunc(*mpDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
1947                 }
1948             }
1949         }
1950     }
1951 
1952     if (bAnyDirty || bAnyChanged)
1953     {
1954         if (bAnyDirty)
1955             mpDoc->EnsureFormulaCellResults(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab), true);
1956 
1957         for (nArrY=0; nArrY<nArrCount; nArrY++)
1958         {
1959             RowInfo* pThisRowInfo = &pRowInfo[nArrY];
1960             for (nX=nX1; nX<=nX2; nX++)
1961             {
1962                 const ScRefCellValue& rCell = pThisRowInfo->cellInfo(nX).maCell;
1963 
1964                 if (rCell.getType() != CELLTYPE_FORMULA)
1965                     continue;
1966 
1967                 ScFormulaCell* pFCell = rCell.getFormula();
1968                 if (pFCell->IsRunning())
1969                     // still being interpreted. Skip it.
1970                     continue;
1971 
1972                 if (!pFCell->IsChanged())
1973                     // the result hasn't changed. Skip it.
1974                     continue;
1975 
1976                 pThisRowInfo->bChanged = true;
1977                 if ( pThisRowInfo->cellInfo(nX).bMerged )
1978                 {
1979                     SCSIZE nOverY = nArrY + 1;
1980                     while ( nOverY<nArrCount &&
1981                             pRowInfo[nOverY].cellInfo(nX).bVOverlapped )
1982                     {
1983                         pRowInfo[nOverY].bChanged = true;
1984                         ++nOverY;
1985                     }
1986                 }
1987             }
1988         }
1989 
1990         if (bAnyDirty)
1991             ScProgress::DeleteInterpretProgress();
1992     }
1993 
1994     mpDoc->EnableIdle(bWasIdleEnabled);
1995 }
1996 
FillReferenceMark(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor)1997 ReferenceMark ScOutputData::FillReferenceMark( SCCOL nRefStartX, SCROW nRefStartY,
1998                                 SCCOL nRefEndX, SCROW nRefEndY, const Color& rColor)
1999 {
2000     ReferenceMark aResult;
2001 
2002     PutInOrder( nRefStartX, nRefEndX );
2003     PutInOrder( nRefStartY, nRefEndY );
2004 
2005     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
2006         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
2007 
2008     if ( nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
2009          nRefStartY <= nVisY2 && nRefEndY >= nVisY1 )
2010     {
2011         tools::Long nMinX = nScrX;
2012         tools::Long nMinY = nScrY;
2013         tools::Long nMaxX = nScrX + nScrW - 1;
2014         tools::Long nMaxY = nScrY + nScrH - 1;
2015         if ( bLayoutRTL )
2016             std::swap( nMinX, nMaxX );
2017         tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2018 
2019         bool bTop    = false;
2020         bool bBottom = false;
2021         bool bLeft   = false;
2022         bool bRight  = false;
2023 
2024         tools::Long nPosY = nScrY;
2025         bool bNoStartY = ( nY1 < nRefStartY );
2026         bool bNoEndY   = false;
2027         for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)      // loop to end for bNoEndY check
2028         {
2029             SCROW nY = pRowInfo[nArrY].nRowNo;
2030 
2031             if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2032             {
2033                 nMinY = nPosY;
2034                 bTop = true;
2035             }
2036             if ( nY==nRefEndY )
2037             {
2038                 nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
2039                 bBottom = true;
2040             }
2041             if ( nY>nRefEndY && bNoEndY )
2042             {
2043                 nMaxY = nPosY-2;
2044                 bBottom = true;
2045             }
2046             bNoStartY = ( nY < nRefStartY );
2047             bNoEndY   = ( nY < nRefEndY );
2048             nPosY += pRowInfo[nArrY].nHeight;
2049         }
2050 
2051         tools::Long nPosX = nScrX;
2052         if ( bLayoutRTL )
2053             nPosX += nMirrorW - 1;      // always in pixels
2054 
2055         for (SCCOL nX=nX1; nX<=nX2; nX++)
2056         {
2057             if ( nX==nRefStartX )
2058             {
2059                 nMinX = nPosX;
2060                 bLeft = true;
2061             }
2062             if ( nX==nRefEndX )
2063             {
2064                 nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
2065                 bRight = true;
2066             }
2067             nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2068         }
2069 
2070         if (bTop && bBottom && bLeft && bRight)
2071         {
2072             // mnPPT[XY] already has the factor aZoom[XY] in it.
2073             aResult = ReferenceMark( nMinX / mnPPTX,
2074                                      nMinY / mnPPTY,
2075                                      ( nMaxX - nMinX ) / mnPPTX,
2076                                      ( nMaxY - nMinY ) / mnPPTY,
2077                                      nTab,
2078                                      rColor );
2079         }
2080     }
2081 
2082     return aResult;
2083 }
2084 
DrawRefMark(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor,bool bHandle)2085 void ScOutputData::DrawRefMark( SCCOL nRefStartX, SCROW nRefStartY,
2086                                 SCCOL nRefEndX, SCROW nRefEndY,
2087                                 const Color& rColor, bool bHandle )
2088 {
2089     PutInOrder( nRefStartX, nRefEndX );
2090     PutInOrder( nRefStartY, nRefEndY );
2091 
2092     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
2093         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
2094     else if (mpDoc->HasAttrib(nRefEndX, nRefEndY, nTab, HasAttrFlags::Merged))
2095         mpDoc->ExtendMerge(nRefEndX, nRefEndY, nRefEndX, nRefEndY, nTab);
2096 
2097     if ( !(nRefStartX <= nVisX2 && nRefEndX >= nVisX1 &&
2098          nRefStartY <= nVisY2 && nRefEndY >= nVisY1) )
2099         return;
2100 
2101     tools::Long nMinX = nScrX;
2102     tools::Long nMinY = nScrY;
2103     tools::Long nMaxX = nScrX + nScrW - 1;
2104     tools::Long nMaxY = nScrY + nScrH - 1;
2105     if ( bLayoutRTL )
2106         std::swap( nMinX, nMaxX );
2107     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2108 
2109     bool bTop    = false;
2110     bool bBottom = false;
2111     bool bLeft   = false;
2112     bool bRight  = false;
2113 
2114     tools::Long nPosY = nScrY;
2115     bool bNoStartY = ( nY1 < nRefStartY );
2116     bool bNoEndY   = false;
2117     for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)      // loop to end for bNoEndY check
2118     {
2119         SCROW nY = pRowInfo[nArrY].nRowNo;
2120 
2121         if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2122         {
2123             nMinY = nPosY;
2124             bTop = true;
2125         }
2126         if ( nY==nRefEndY )
2127         {
2128             nMaxY = nPosY + pRowInfo[nArrY].nHeight - 2;
2129             bBottom = true;
2130         }
2131         if ( nY>nRefEndY && bNoEndY )
2132         {
2133             nMaxY = nPosY-2;
2134             bBottom = true;
2135         }
2136         bNoStartY = ( nY < nRefStartY );
2137         bNoEndY   = ( nY < nRefEndY );
2138         nPosY += pRowInfo[nArrY].nHeight;
2139     }
2140 
2141     tools::Long nPosX = nScrX;
2142     if ( bLayoutRTL )
2143         nPosX += nMirrorW - 1;      // always in pixels
2144 
2145     for (SCCOL nX=nX1; nX<=nX2; nX++)
2146     {
2147         if ( nX==nRefStartX )
2148         {
2149             nMinX = nPosX;
2150             bLeft = true;
2151         }
2152         if ( nX==nRefEndX )
2153         {
2154             nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 2 ) * nLayoutSign;
2155             bRight = true;
2156         }
2157         nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2158     }
2159 
2160     if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
2161         return;
2162 
2163     mpDev->SetLineColor( rColor );
2164     if (bTop && bBottom && bLeft && bRight && !comphelper::LibreOfficeKit::isActive() )
2165     {
2166             mpDev->SetFillColor();
2167             mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
2168     }
2169     else if ( !comphelper::LibreOfficeKit::isActive() )
2170     {
2171         if (bTop)
2172             mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMaxX, nMinY ) );
2173         if (bBottom)
2174             mpDev->DrawLine( Point( nMinX, nMaxY ), Point( nMaxX, nMaxY ) );
2175         if (bLeft)
2176             mpDev->DrawLine( Point( nMinX, nMinY ), Point( nMinX, nMaxY ) );
2177         if (bRight)
2178             mpDev->DrawLine( Point( nMaxX, nMinY ), Point( nMaxX, nMaxY ) );
2179     }
2180     if ( !bHandle || !bRight || !bBottom || comphelper::LibreOfficeKit::isActive() )
2181         return;
2182 
2183     mpDev->SetLineColor( rColor );
2184     mpDev->SetFillColor( rColor );
2185 
2186     const sal_Int32 aRadius = 4;
2187 
2188     sal_Int32 aRectMaxX1 = nMaxX - nLayoutSign * aRadius;
2189     sal_Int32 aRectMaxX2 = nMaxX + nLayoutSign;
2190     sal_Int32 aRectMinX1 = nMinX - nLayoutSign;
2191     sal_Int32 aRectMinX2 = nMinX + nLayoutSign * aRadius;
2192 
2193     sal_Int32 aRectMaxY1 = nMaxY - aRadius;
2194     sal_Int32 aRectMaxY2 = nMaxY + 1;
2195     sal_Int32 aRectMinY1 = nMinY - 1;
2196     sal_Int32 aRectMinY2 = nMinY + aRadius;
2197 
2198     // Draw corner rectangles
2199     tools::Rectangle aLowerRight( aRectMaxX1, aRectMaxY1, aRectMaxX2, aRectMaxY2 );
2200     tools::Rectangle aUpperLeft ( aRectMinX1, aRectMinY1, aRectMinX2, aRectMinY2 );
2201     tools::Rectangle aLowerLeft ( aRectMinX1, aRectMaxY1, aRectMinX2, aRectMaxY2 );
2202     tools::Rectangle aUpperRight( aRectMaxX1, aRectMinY1, aRectMaxX2, aRectMinY2 );
2203 
2204     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerRight ) ), lclCornerRectTransparency );
2205     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperLeft  ) ), lclCornerRectTransparency );
2206     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aLowerLeft  ) ), lclCornerRectTransparency );
2207     mpDev->DrawTransparent( tools::PolyPolygon( tools::Polygon( aUpperRight ) ), lclCornerRectTransparency );
2208 }
2209 
DrawOneChange(SCCOL nRefStartX,SCROW nRefStartY,SCCOL nRefEndX,SCROW nRefEndY,const Color & rColor,sal_uInt16 nType)2210 void ScOutputData::DrawOneChange( SCCOL nRefStartX, SCROW nRefStartY,
2211                                 SCCOL nRefEndX, SCROW nRefEndY,
2212                                 const Color& rColor, sal_uInt16 nType )
2213 {
2214     PutInOrder( nRefStartX, nRefEndX );
2215     PutInOrder( nRefStartY, nRefEndY );
2216 
2217     if ( nRefStartX == nRefEndX && nRefStartY == nRefEndY )
2218         mpDoc->ExtendMerge( nRefStartX, nRefStartY, nRefEndX, nRefEndY, nTab );
2219 
2220     if ( !(nRefStartX <= nVisX2 + 1 && nRefEndX >= nVisX1 &&
2221          nRefStartY <= nVisY2 + 1 && nRefEndY >= nVisY1) )       // +1 because it touches next cells left/top
2222         return;
2223 
2224     tools::Long nMinX = nScrX;
2225     tools::Long nMinY = nScrY;
2226     tools::Long nMaxX = nScrX+nScrW-1;
2227     tools::Long nMaxY = nScrY+nScrH-1;
2228     if ( bLayoutRTL )
2229         std::swap( nMinX, nMaxX );
2230     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2231 
2232     bool bTop    = false;
2233     bool bBottom = false;
2234     bool bLeft   = false;
2235     bool bRight  = false;
2236 
2237     tools::Long nPosY = nScrY;
2238     bool bNoStartY = ( nY1 < nRefStartY );
2239     bool bNoEndY   = false;
2240     for (SCSIZE nArrY=1; nArrY<nArrCount; nArrY++)      // loop to end for bNoEndY check
2241     {
2242         SCROW nY = pRowInfo[nArrY].nRowNo;
2243 
2244         if ( nY==nRefStartY || (nY>nRefStartY && bNoStartY) )
2245         {
2246             nMinY = nPosY - 1;
2247             bTop = true;
2248         }
2249         if ( nY==nRefEndY )
2250         {
2251             nMaxY = nPosY + pRowInfo[nArrY].nHeight - 1;
2252             bBottom = true;
2253         }
2254         if ( nY>nRefEndY && bNoEndY )
2255         {
2256             nMaxY = nPosY - 1;
2257             bBottom = true;
2258         }
2259         bNoStartY = ( nY < nRefStartY );
2260         bNoEndY   = ( nY < nRefEndY );
2261         nPosY += pRowInfo[nArrY].nHeight;
2262     }
2263 
2264     tools::Long nPosX = nScrX;
2265     if ( bLayoutRTL )
2266         nPosX += nMirrorW - 1;      // always in pixels
2267 
2268     for (SCCOL nX=nX1; nX<=nX2+1; nX++)
2269     {
2270         if ( nX==nRefStartX )
2271         {
2272             nMinX = nPosX - nLayoutSign;
2273             bLeft = true;
2274         }
2275         if ( nX==nRefEndX )
2276         {
2277             nMaxX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1 ) * nLayoutSign;
2278             bRight = true;
2279         }
2280         nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2281     }
2282 
2283     if ( nMaxX * nLayoutSign < nMinX * nLayoutSign || nMaxY < nMinY )
2284         return;
2285 
2286     if ( nType == SC_CAT_DELETE_ROWS )
2287         bLeft = bRight = bBottom = false;       //! thick lines???
2288     else if ( nType == SC_CAT_DELETE_COLS )
2289         bTop = bBottom = bRight = false;        //! thick lines???
2290 
2291     mpDev->SetLineColor( rColor );
2292     if (bTop && bBottom && bLeft && bRight)
2293     {
2294         mpDev->SetFillColor();
2295         mpDev->DrawRect( tools::Rectangle( nMinX, nMinY, nMaxX, nMaxY ) );
2296     }
2297     else
2298     {
2299         if (bTop)
2300         {
2301             mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMaxX,nMinY ) );
2302             if ( nType == SC_CAT_DELETE_ROWS )
2303                 mpDev->DrawLine( Point( nMinX,nMinY+1 ), Point( nMaxX,nMinY+1 ) );
2304         }
2305         if (bBottom)
2306             mpDev->DrawLine( Point( nMinX,nMaxY ), Point( nMaxX,nMaxY ) );
2307         if (bLeft)
2308         {
2309             mpDev->DrawLine( Point( nMinX,nMinY ), Point( nMinX,nMaxY ) );
2310             if ( nType == SC_CAT_DELETE_COLS )
2311                 mpDev->DrawLine( Point( nMinX+nLayoutSign,nMinY ), Point( nMinX+nLayoutSign,nMaxY ) );
2312         }
2313         if (bRight)
2314             mpDev->DrawLine( Point( nMaxX,nMinY ), Point( nMaxX,nMaxY ) );
2315     }
2316     if ( bLeft && bTop )
2317     {
2318         mpDev->SetLineColor();
2319         mpDev->SetFillColor( rColor );
2320         mpDev->DrawRect( tools::Rectangle( nMinX+nLayoutSign, nMinY+1, nMinX+3*nLayoutSign, nMinY+3 ) );
2321     }
2322 }
2323 
DrawChangeTrack()2324 void ScOutputData::DrawChangeTrack()
2325 {
2326     ScChangeTrack* pTrack = mpDoc->GetChangeTrack();
2327     ScChangeViewSettings* pSettings = mpDoc->GetChangeViewSettings();
2328     if ( !pTrack || !pTrack->GetFirst() || !pSettings || !pSettings->ShowChanges() )
2329         return;         // nothing there or hidden
2330 
2331     ScActionColorChanger aColorChanger(*pTrack);
2332 
2333     //  clipping happens from the outside
2334     //! without clipping, only paint affected cells ??!??!?
2335 
2336     SCCOL nEndX = nX2;
2337     SCROW nEndY = nY2;
2338     if ( nEndX < mpDoc->MaxCol() ) ++nEndX;      // also from the next cell since the mark
2339     if ( nEndY < mpDoc->MaxRow() ) ++nEndY;      // protrudes from the preceding cell
2340     ScRange aViewRange( nX1, nY1, nTab, nEndX, nEndY, nTab );
2341     const ScChangeAction* pAction = pTrack->GetFirst();
2342     while (pAction)
2343     {
2344         if ( pAction->IsVisible() )
2345         {
2346             ScChangeActionType eActionType = pAction->GetType();
2347             const ScBigRange& rBig = pAction->GetBigRange();
2348             if ( rBig.aStart.Tab() == nTab )
2349             {
2350                 ScRange aRange = rBig.MakeRange( *mpDoc );
2351 
2352                 if ( eActionType == SC_CAT_DELETE_ROWS )
2353                     aRange.aEnd.SetRow( aRange.aStart.Row() );
2354                 else if ( eActionType == SC_CAT_DELETE_COLS )
2355                     aRange.aEnd.SetCol( aRange.aStart.Col() );
2356 
2357                 if ( aRange.Intersects( aViewRange ) &&
2358                      ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
2359                 {
2360                     aColorChanger.Update( *pAction );
2361                     Color aColor( aColorChanger.GetColor() );
2362                     DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
2363                                     aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
2364 
2365                 }
2366             }
2367             if ( eActionType == SC_CAT_MOVE &&
2368                     static_cast<const ScChangeActionMove*>(pAction)->
2369                         GetFromRange().aStart.Tab() == nTab )
2370             {
2371                 ScRange aRange = static_cast<const ScChangeActionMove*>(pAction)->
2372                         GetFromRange().MakeRange( *mpDoc );
2373                 if ( aRange.Intersects( aViewRange ) &&
2374                      ScViewUtil::IsActionShown( *pAction, *pSettings, *mpDoc ) )
2375                 {
2376                     aColorChanger.Update( *pAction );
2377                     Color aColor( aColorChanger.GetColor() );
2378                     DrawOneChange( aRange.aStart.Col(), aRange.aStart.Row(),
2379                                     aRange.aEnd.Col(), aRange.aEnd.Row(), aColor, sal::static_int_cast<sal_uInt16>(eActionType) );
2380                 }
2381             }
2382         }
2383 
2384         pAction = pAction->GetNext();
2385     }
2386 }
2387 
DrawSparklines(vcl::RenderContext & rRenderContext)2388 void ScOutputData::DrawSparklines(vcl::RenderContext& rRenderContext)
2389 {
2390     Size aOnePixel = rRenderContext.PixelToLogic(Size(1,1));
2391     tools::Long nOneXLogic = aOnePixel.Width();
2392     tools::Long nOneYLogic = aOnePixel.Height();
2393 
2394     // See more about bWorksInPixels in ScOutputData::DrawGrid
2395     bool bWorksInPixels = false;
2396     if (eType == OUTTYPE_WINDOW)
2397         bWorksInPixels = true;
2398 
2399     tools::Long nOneX = 1;
2400     tools::Long nOneY = 1;
2401     if (!bWorksInPixels)
2402     {
2403         nOneX = nOneXLogic;
2404         nOneY = nOneYLogic;
2405     }
2406 
2407     tools::Long nInitPosX = nScrX;
2408     if ( bLayoutRTL )
2409         nInitPosX += nMirrorW - 1;              // always in pixels
2410     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2411 
2412     tools::Long nPosY = nScrY;
2413     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
2414     {
2415         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2416         if ( pThisRowInfo->bChanged )
2417         {
2418             tools::Long nPosX = nInitPosX;
2419             for (SCCOL nX=nX1; nX<=nX2; nX++)
2420             {
2421                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2422                 bool bIsMerged = false;
2423 
2424                 if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
2425                 {
2426                     // find start of merged cell
2427                     bIsMerged = true;
2428                     SCROW nY = pRowInfo[nArrY].nRowNo;
2429                     SCCOL nMergeX = nX;
2430                     SCROW nMergeY = nY;
2431                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
2432                 }
2433 
2434                 std::shared_ptr<sc::Sparkline> pSparkline;
2435                 ScAddress aCurrentAddress(nX, pRowInfo[nArrY].nRowNo, nTab);
2436 
2437                 if (!mpDoc->ColHidden(nX, nTab) && (pSparkline = mpDoc->GetSparkline(aCurrentAddress))
2438                     && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
2439                 {
2440                     const tools::Long nWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
2441                     const tools::Long nHeight = pThisRowInfo->nHeight;
2442 
2443                     Point aPoint(nPosX, nPosY);
2444                     Size aSize(nWidth, nHeight);
2445 
2446                     sc::SparklineRenderer renderer(*mpDoc);
2447                     renderer.render(pSparkline, rRenderContext, tools::Rectangle(aPoint, aSize), nOneX, nOneY, double(aZoomX), double(aZoomY));
2448                 }
2449 
2450                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2451             }
2452         }
2453         nPosY += pThisRowInfo->nHeight;
2454     }
2455 
2456 }
2457 
2458 //TODO: moggi Need to check if this can't be written simpler
DrawNoteMarks(vcl::RenderContext & rRenderContext)2459 void ScOutputData::DrawNoteMarks(vcl::RenderContext& rRenderContext)
2460 {
2461     // cool#6911 draw the note indicator browser-side instead
2462     if (comphelper::LibreOfficeKit::isActive())
2463         return;
2464 
2465     tools::Long nInitPosX = nScrX;
2466     if ( bLayoutRTL )
2467         nInitPosX += nMirrorW - 1;              // always in pixels
2468     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2469 
2470     tools::Long nPosY = nScrY - 1;
2471     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
2472     {
2473         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2474         if ( pThisRowInfo->bChanged )
2475         {
2476             tools::Long nPosX = nInitPosX;
2477             for (SCCOL nX=nX1; nX<=nX2; nX++)
2478             {
2479                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2480                 bool bIsMerged = false;
2481 
2482                 if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
2483                 {
2484                     // find start of merged cell
2485                     bIsMerged = true;
2486                     SCROW nY = pRowInfo[nArrY].nRowNo;
2487                     SCCOL nMergeX = nX;
2488                     SCROW nMergeY = nY;
2489                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
2490                 }
2491 
2492                 if (!mpDoc->ColHidden(nX, nTab) && mpDoc->GetNote(nX, pRowInfo[nArrY].nRowNo, nTab)
2493                     && (bIsMerged || (!pInfo->bHOverlapped && !pInfo->bVOverlapped)))
2494                 {
2495                     rRenderContext.SetLineColor(SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCGRID).nColor);
2496 
2497                     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2498                     if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
2499                         rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
2500                     else
2501                         rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCCOMMENTS).nColor );
2502 
2503                     tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - 1) * nLayoutSign;
2504                     if ( bIsMerged || pInfo->bMerged )
2505                     {
2506                         //  if merged, add widths of all cells
2507                         SCCOL nNextX = nX + 1;
2508                         while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
2509                         {
2510                             nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
2511                             ++nNextX;
2512                         }
2513                     }
2514                     // DPI/ZOOM 100/100 => 6, 100/50 => 4.5, 100/150 => 7.5
2515                     // DPI/ZOOM 150/100 => 7.5, 150/50 => 6, 150/150 => 9
2516                     sal_Int16 nSize = officecfg::Office::Calc::Content::Display::NoteIndicator::get();
2517                     if (nSize < 1)
2518                     {
2519                        const double fSize(rRenderContext.GetDPIScaleFactor() * aZoomX * 3 + 3);
2520                        nSize = static_cast<sal_Int16>(fSize);
2521                     }
2522                     Point aPoints[3];
2523                     aPoints[0] = Point(nMarkX, nPosY);
2524                     aPoints[0].setX( bLayoutRTL ? aPoints[0].X() + nSize : aPoints[0].X() - nSize );
2525                     aPoints[1] = Point(nMarkX, nPosY);
2526                     aPoints[2] = Point(nMarkX, nPosY + nSize);
2527                     tools::Polygon aPoly(3, aPoints);
2528 
2529                     if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
2530                         rRenderContext.DrawPolygon(aPoly);
2531                 }
2532 
2533                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2534             }
2535         }
2536         nPosY += pThisRowInfo->nHeight;
2537     }
2538 }
2539 
DrawFormulaMarks(vcl::RenderContext & rRenderContext)2540 void ScOutputData::DrawFormulaMarks(vcl::RenderContext& rRenderContext)
2541 {
2542     bool bFirst = true;
2543 
2544     tools::Long nInitPosX = nScrX;
2545     if ( bLayoutRTL )
2546         nInitPosX += nMirrorW - 1;              // always in pixels
2547     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2548 
2549     tools::Long nPosY = nScrY;
2550     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
2551     {
2552         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2553         if ( pThisRowInfo->bChanged )
2554         {
2555             tools::Long nPosX = nInitPosX;
2556             for (SCCOL nX=nX1; nX<=nX2; nX++)
2557             {
2558                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2559                 if (!mpDoc->ColHidden(nX, nTab) && !mpDoc->GetFormula(nX, pRowInfo[nArrY].nRowNo, nTab).isEmpty()
2560                     && (!pInfo->bHOverlapped && !pInfo->bVOverlapped))
2561                 {
2562                     if (bFirst)
2563                     {
2564                         rRenderContext.SetLineColor(COL_WHITE);
2565 
2566                         const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2567                         if ( mbUseStyleColor && rStyleSettings.GetHighContrastMode() )
2568                             rRenderContext.SetFillColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
2569                         else
2570                             rRenderContext.SetFillColor(COL_LIGHTBLUE);
2571 
2572                         bFirst = false;
2573                     }
2574 
2575                     tools::Long nMarkX = nPosX;
2576                     tools::Long nMarkY = nPosY + pThisRowInfo->nHeight - 2;
2577                     if ( pInfo->bMerged )
2578                     {
2579                         for (SCSIZE nNextY=nArrY+1; nNextY+1<nArrCount; nNextY++)
2580                         {
2581                             bool bVOver;
2582                             if (pRowInfo[nNextY + 1].nRowNo == (pRowInfo[nNextY].nRowNo + 1)) {
2583                                 bVOver = pRowInfo[nNextY].cellInfo(nX).bVOverlapped;
2584                             } else {
2585                                 bVOver = mpDoc->GetAttr(nX,nNextY,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped();
2586                             }
2587                             if (!bVOver) break;
2588                             nMarkY += pRowInfo[nNextY].nHeight;
2589                         }
2590                     }
2591                     // DPI/ZOOM 100/100 => 10, 100/50 => 7, 100/150 => 13
2592                     // DPI/ZOOM 150/100 => 13, 150/50 => 8.5, 150/150 => 17.5
2593                     const double nSize( rRenderContext.GetDPIScaleFactor() * aZoomX * 6 + 4);
2594                     Point aPoints[3];
2595                     aPoints[0] = Point(nMarkX, nMarkY);
2596                     aPoints[0].setX( bLayoutRTL ? aPoints[0].X() - nSize : aPoints[0].X() + nSize );
2597                     aPoints[1] = Point(nMarkX, nMarkY);
2598                     aPoints[2] = Point(nMarkX, nMarkY - nSize);
2599                     tools::Polygon aPoly(3, aPoints);
2600 
2601                     if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
2602                         rRenderContext.DrawPolygon(aPoly);
2603                 }
2604 
2605                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2606             }
2607         }
2608         nPosY += pThisRowInfo->nHeight;
2609     }
2610 }
2611 
AddPDFNotes()2612 void ScOutputData::AddPDFNotes()
2613 {
2614     vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >( mpDev->GetExtOutDevData() );
2615     if ( !pPDFData || !pPDFData->GetIsExportNotes() )
2616         return;
2617 
2618     tools::Long nInitPosX = nScrX;
2619     if ( bLayoutRTL )
2620     {
2621         Size aOnePixel = mpDev->PixelToLogic(Size(1,1));
2622         tools::Long nOneX = aOnePixel.Width();
2623         nInitPosX += nMirrorW - nOneX;
2624     }
2625     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2626 
2627     tools::Long nPosY = nScrY;
2628     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
2629     {
2630         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2631         if ( pThisRowInfo->bChanged )
2632         {
2633             tools::Long nPosX = nInitPosX;
2634             for (SCCOL nX=nX1; nX<=nX2; nX++)
2635             {
2636                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2637                 bool bIsMerged = false;
2638                 SCROW nY = pRowInfo[nArrY].nRowNo;
2639                 SCCOL nMergeX = nX;
2640                 SCROW nMergeY = nY;
2641 
2642                 if ( nX==nX1 && pInfo->bHOverlapped && !pInfo->bVOverlapped )
2643                 {
2644                     // find start of merged cell
2645                     bIsMerged = true;
2646                     mpDoc->ExtendOverlapped( nMergeX, nMergeY, nX, nY, nTab );
2647                     // use origin's pCell for NotePtr test below
2648                 }
2649 
2650                 const ScPostIt* pNote = mpDoc->GetNote(nMergeX, nMergeY, nTab);
2651 
2652                 if ( pNote && ( bIsMerged || ( !pInfo->bHOverlapped && !pInfo->bVOverlapped ) ) )
2653                 {
2654                     tools::Long nNoteWidth = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
2655                     tools::Long nNoteHeight = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTY );
2656 
2657                     tools::Long nMarkX = nPosX + ( pRowInfo[0].basicCellInfo(nX).nWidth - nNoteWidth ) * nLayoutSign;
2658                     if ( bIsMerged || pInfo->bMerged )
2659                     {
2660                         //  if merged, add widths of all cells
2661                         SCCOL nNextX = nX + 1;
2662                         while ( nNextX <= nX2 + 1 && pThisRowInfo->cellInfo(nNextX).bHOverlapped )
2663                         {
2664                             nMarkX += pRowInfo[0].basicCellInfo(nNextX).nWidth * nLayoutSign;
2665                             ++nNextX;
2666                         }
2667                     }
2668                     if ( bLayoutRTL ? ( nMarkX >= 0 ) : ( nMarkX < nScrX+nScrW ) )
2669                     {
2670                         tools::Rectangle aNoteRect( nMarkX, nPosY, nMarkX+nNoteWidth*nLayoutSign, nPosY+nNoteHeight );
2671 
2672                         vcl::pdf::PDFNote aNote;
2673 
2674                         // Note title is the cell address (as on printed note pages)
2675                         ScAddress aAddress( nMergeX, nMergeY, nTab );
2676                         aNote.maTitle = aAddress.Format(ScRefFlags::VALID, mpDoc, mpDoc->GetAddressConvention());
2677 
2678                         // Content has to be a simple string without line breaks
2679                         OUString aContent = pNote->GetText();
2680                         aNote.maContents = aContent.replaceAll("\n", " ");
2681 
2682                         pPDFData->CreateNote( aNoteRect, aNote );
2683                     }
2684                 }
2685 
2686                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2687             }
2688         }
2689         nPosY += pThisRowInfo->nHeight;
2690     }
2691 }
2692 
DrawClipMarks()2693 void ScOutputData::DrawClipMarks()
2694 {
2695     if (!bAnyClipped)
2696         return;
2697 
2698     Color aArrowFillCol( SC_MOD()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).nColor );
2699     const bool bIsDarkBackground = SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor.IsDark();
2700 
2701     DrawModeFlags nOldDrawMode = mpDev->GetDrawMode();
2702 
2703     tools::Long nInitPosX = nScrX;
2704     if ( bLayoutRTL )
2705         nInitPosX += nMirrorW - 1;              // always in pixels
2706     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2707 
2708     tools::Rectangle aCellRect;
2709     tools::Long nPosY = nScrY;
2710     for (SCSIZE nArrY=1; nArrY+1<nArrCount; nArrY++)
2711     {
2712         RowInfo* pThisRowInfo = &pRowInfo[nArrY];
2713         if ( pThisRowInfo->bChanged )
2714         {
2715             SCROW nY = pThisRowInfo->nRowNo;
2716             tools::Long nPosX = nInitPosX;
2717             for (SCCOL nX=nX1; nX<=nX2; nX++)
2718             {
2719                 ScCellInfo* pInfo = &pThisRowInfo->cellInfo(nX);
2720                 if (pInfo->nClipMark != ScClipMark::NONE)
2721                 {
2722                     if (pInfo->bHOverlapped || pInfo->bVOverlapped)
2723                     {
2724                         //  merge origin may be outside of visible area - use document functions
2725 
2726                         SCCOL nOverX = nX;
2727                         SCROW nOverY = nY;
2728                         tools::Long nStartPosX = nPosX;
2729                         tools::Long nStartPosY = nPosY;
2730 
2731                         while ( nOverX > 0 && ( mpDoc->GetAttr(
2732                                 nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Hor ) )
2733                         {
2734                             --nOverX;
2735                             nStartPosX -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
2736                         }
2737 
2738                         while ( nOverY > 0 && ( mpDoc->GetAttr(
2739                                 nOverX, nOverY, nTab, ATTR_MERGE_FLAG )->GetValue() & ScMF::Ver ) )
2740                         {
2741                             --nOverY;
2742                             nStartPosY -= nLayoutSign * static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
2743                         }
2744 
2745                         tools::Long nOutWidth = static_cast<tools::Long>( mpDoc->GetColWidth(nOverX,nTab) * mnPPTX );
2746                         tools::Long nOutHeight = static_cast<tools::Long>( mpDoc->GetRowHeight(nOverY,nTab) * mnPPTY );
2747 
2748                         const ScMergeAttr* pMerge = mpDoc->GetAttr( nOverX, nOverY, nTab, ATTR_MERGE );
2749                         SCCOL nCountX = pMerge->GetColMerge();
2750                         for (SCCOL i=1; i<nCountX; i++)
2751                             nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
2752                         SCROW nCountY = pMerge->GetRowMerge();
2753                         nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
2754 
2755                         if ( bLayoutRTL )
2756                             nStartPosX -= nOutWidth - 1;
2757                         aCellRect = tools::Rectangle( Point( nStartPosX, nStartPosY ), Size( nOutWidth, nOutHeight ) );
2758                     }
2759                     else
2760                     {
2761                         tools::Long nOutWidth = pRowInfo[0].basicCellInfo(nX).nWidth;
2762                         tools::Long nOutHeight = pThisRowInfo->nHeight;
2763 
2764                         if ( pInfo->bMerged && pInfo->pPatternAttr )
2765                         {
2766                             SCCOL nOverX = nX;
2767                             SCROW nOverY = nY;
2768                             const ScMergeAttr* pMerge =
2769                                     &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
2770                             SCCOL nCountX = pMerge->GetColMerge();
2771                             for (SCCOL i=1; i<nCountX; i++)
2772                                 nOutWidth += mpDoc->GetColWidth(nOverX+i,nTab) * mnPPTX;
2773                             SCROW nCountY = pMerge->GetRowMerge();
2774                             nOutHeight += mpDoc->GetScaledRowHeight( nOverY+1, nOverY+nCountY-1, nTab, mnPPTY);
2775                         }
2776 
2777                         tools::Long nStartPosX = nPosX;
2778                         if ( bLayoutRTL )
2779                             nStartPosX -= nOutWidth - 1;
2780                         // #i80447# create aCellRect from two points in case nOutWidth is 0
2781                         aCellRect = tools::Rectangle( Point( nStartPosX, nPosY ),
2782                                                Point( nStartPosX+nOutWidth-1, nPosY+nOutHeight-1 ) );
2783                     }
2784 
2785                     aCellRect.AdjustBottom( -1 );    // don't paint over the cell grid
2786                     if ( bLayoutRTL )
2787                         aCellRect.AdjustLeft(1 );
2788                     else
2789                         aCellRect.AdjustRight( -1 );
2790 
2791                     tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * mnPPTX );
2792                     Size aMarkSize( nMarkPixel, (nMarkPixel-1)*2 );
2793 
2794                     const Color aColor = pInfo->maBackground ?
2795                         static_cast<const SvxBrushItem*>(pInfo->maBackground.getItem())->GetColor() :
2796                         COL_AUTO;
2797                     if ( aColor == COL_AUTO ? bIsDarkBackground : aColor.IsDark() )
2798                         mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::WhiteLine );
2799                     else
2800                         mpDev->SetDrawMode( nOldDrawMode | DrawModeFlags::BlackLine );
2801 
2802                     if (bVertical)
2803                     {
2804                         if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Bottom : ScClipMark::Top))
2805                         {
2806                             //  visually top
2807                             tools::Rectangle aMarkRect = aCellRect;
2808                             aMarkRect.SetBottom(aCellRect.Top() + nMarkPixel - 1);
2809                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true, true);
2810                         }
2811                         if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Top : ScClipMark::Bottom))
2812                         {
2813                             //  visually bottom
2814                             tools::Rectangle aMarkRect = aCellRect;
2815                             aMarkRect.SetTop(aCellRect.Bottom() + nMarkPixel + 1);
2816                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
2817                                 true);
2818                         }
2819                     }
2820                     else
2821                     {
2822                         if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Right : ScClipMark::Left))
2823                         {
2824                             //  visually left
2825                             tools::Rectangle aMarkRect = aCellRect;
2826                             aMarkRect.SetRight(aCellRect.Left() + nMarkPixel - 1);
2827                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, true,
2828                                 false);
2829                         }
2830                         if (pInfo->nClipMark & (bLayoutRTL ? ScClipMark::Left : ScClipMark::Right))
2831                         {
2832                             //  visually right
2833                             tools::Rectangle aMarkRect = aCellRect;
2834                             aMarkRect.SetLeft(aCellRect.Right() - nMarkPixel + 1);
2835                             SvxFont::DrawArrow(*mpDev, aMarkRect, aMarkSize, aArrowFillCol, false,
2836                                 false);
2837                         }
2838                     }
2839                 }
2840                 nPosX += pRowInfo[0].basicCellInfo(nX).nWidth * nLayoutSign;
2841             }
2842         }
2843         nPosY += pThisRowInfo->nHeight;
2844     }
2845 
2846     mpDev->SetDrawMode(nOldDrawMode);
2847 }
2848 
2849 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2850