/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// draw text into upper left corner
rSalLayout.DrawBase().adjustX(-aBoundRect.Left());
rSalLayout.DrawBase().adjustY(-aBoundRect.Top());
rSalLayout.DrawText( *pVDev->mpGraphics );
void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
{
Color aOldColor = GetTextColor();
Color aOldTextLineColor = GetTextLineColor();
Color aOldOverlineColor = GetOverlineColor();
FontRelief eRelief = maFont.GetRelief();
basegfx::B2DPoint aOrigPos = rSalLayout.DrawBase(); if ( eRelief != FontRelief::NONE )
{
Color aReliefColor( COL_LIGHTGRAY );
Color aTextColor( aOldColor );
Color aTextLineColor( aOldTextLineColor );
Color aOverlineColor( aOldOverlineColor );
// we don't have an automatic color, so black is always drawn on white if ( aTextColor == COL_BLACK )
aTextColor = COL_WHITE; if ( aTextLineColor == COL_BLACK )
aTextLineColor = COL_WHITE; if ( aOverlineColor == COL_BLACK )
aOverlineColor = COL_WHITE;
// relief-color is black for white text, in all other cases // we set this to LightGray // coverity[copy_paste_error: FALSE] - this is intentional if ( aTextColor == COL_WHITE )
aReliefColor = COL_BLACK;
SetTextLineColor( aReliefColor );
SetOverlineColor( aReliefColor );
SetTextColor( aReliefColor );
ImplInitTextColor();
// calculate offset - for high resolution printers the offset // should be greater so that the effect is visible
tools::Long nOff = 1;
nOff += mnDPIX/300;
if ( eRelief == FontRelief::Engraved )
nOff = -nOff;
float OutputDevice::approximate_char_width() const
{ //note pango uses "The quick brown fox jumps over the lazy dog." for english //and has a bunch of per-language strings which corresponds somewhat with //makeRepresentativeText in include/svtools/sampletext.hxx return GetTextWidth(u"aemnnxEM"_ustr) / 8.0;
}
if( !pSalLayout )
{ // The caller expects this to init the elements of pDXAry. // Adapting all the callers to check that GetTextArray succeeded seems // too much work. // Init here to 0 only in the (rare) error case, so that any missing // element init in the happy case will still be found by tools, // and hope that is sufficient. if (pDXAry)
{
pDXAry->resize(nPartLen);
std::fill(pDXAry->begin(), pDXAry->end(), 0);
}
// Fall back to the unbounded DX array when there is no expanded layout context. This is // necessary for certain situations where characters are appended to the input string, such as // automatic ellipsis. if (nIndex == nPartIndex && nLen == nPartLen)
{
nWidth = pSalLayout->FillDXArray(pDXPixelArray, bCaret ? rStr : OUString());
} else
{
nWidth = pSalLayout->FillPartialDXArray(pDXPixelArray, bCaret ? rStr : OUString(),
nPartIndex - nIndex, nPartLen);
}
// convert virtual char widths to virtual absolute positions if( pDXPixelArray )
{ for (int i = 1; i < nPartLen; ++i)
{
(*pDXPixelArray)[i] += (*pDXPixelArray)[i - 1];
}
}
// convert from font units to logical units if (pDXPixelArray)
{
assert(pKernArray && "pDXPixelArray depends on pKernArray existing"); if (mbMap)
{ for (int i = 0; i < nPartLen; ++i)
(*pDXPixelArray)[i] = ImplDevicePixelToLogicWidthDouble((*pDXPixelArray)[i]);
}
}
if (pDXAry)
{
pDXAry->resize(nPartLen); for (int i = 0; i < nPartLen; ++i)
(*pDXAry)[i] = (*pDXPixelArray)[i];
}
vcl::TextArrayMetrics stReturnValue;
if (bBounds)
{
basegfx::B2DRectangle stRect; if (pSalLayout->GetBoundRect(stRect))
{ auto stRect2 = SalLayout::BoundRect2Rectangle(stRect);
stReturnValue.aBounds = ImplDevicePixelToLogic(stRect2);
}
}
// fixup unknown caret positions int i; for (i = 0; i < nCaretPos; ++i) if (aCaretPixelPos[i] >= 0) break; double nXPos = (i < nCaretPos) ? aCaretPixelPos[i] : -1; for (i = 0; i < nCaretPos; ++i)
{ if (aCaretPixelPos[i] >= 0)
nXPos = aCaretPixelPos[i]; else
aCaretPixelPos[i] = nXPos;
}
// handle window mirroring if( IsRTLEnabled() )
{ double nWidth = pSalLayout->GetTextWidth(); for (i = 0; i < nCaretPos; ++i)
aCaretPixelPos[i] = nWidth - aCaretPixelPos[i] - 1;
}
// convert from font units to logical units if( mbMap )
{ for (i = 0; i < nCaretPos; ++i)
aCaretPixelPos[i] = ImplDevicePixelToLogicWidthDouble(aCaretPixelPos[i]);
}
for (i = 0; i < nCaretPos; ++i)
rCaretPos[i] = aCaretPixelPos[i];
}
SalLayoutFlags OutputDevice::GetBiDiLayoutFlags( std::u16string_view rStr, const sal_Int32 nMinIndex, const sal_Int32 nEndIndex ) const
{
SalLayoutFlags nLayoutFlags = SalLayoutFlags::NONE; if( mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiRtl )
nLayoutFlags |= SalLayoutFlags::BiDiRtl; if( mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiStrong )
nLayoutFlags |= SalLayoutFlags::BiDiStrong; elseif( !(mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiRtl) )
{ // Disable Bidi if no RTL hint and only known LTR codes used. bool bAllLtr = true; for (sal_Int32 i = nMinIndex; i < nEndIndex; i++)
{ // [0x0000, 0x052F] are Latin, Greek and Cyrillic. // [0x0370, 0x03FF] has a few holes as if Unicode 10.0.0, but // hopefully no RTL character will be encoded there. if (rStr[i] > 0x052F)
{
bAllLtr = false; break;
}
} if (bAllLtr)
nLayoutFlags |= SalLayoutFlags::BiDiStrong;
} return nLayoutFlags;
}
std::unique_ptr<SalLayout> OutputDevice::ImplLayout( const OUString& rOrigStr, sal_Int32 nMinIndex, sal_Int32 nLen, const Point& rLogicalPos,
tools::Long nLogicalWidth, KernArraySpan pDXArray, std::span<const sal_Bool> pKashidaArray,
SalLayoutFlags flags, vcl::text::TextLayoutCache const* pLayoutCache, const SalLayoutGlyphs* pGlyphs, std::optional<sal_Int32> nDrawOriginCluster,
std::optional<sal_Int32> nDrawMinCharPos, std::optional<sal_Int32> nDrawEndCharPos) const
{ if (pGlyphs && !pGlyphs->IsValid())
{
SAL_WARN("vcl", "Trying to setup invalid cached glyphs - falling back to relayout!");
pGlyphs = nullptr;
} #ifdef DBG_UTIL if (pGlyphs)
{ for( int level = 0;; ++level )
{
SalLayoutGlyphsImpl* glyphsImpl = pGlyphs->Impl(level); if(glyphsImpl == nullptr) break; // It is allowed to reuse only glyphs created with SalLayoutFlags::GlyphItemsOnly. // If the glyphs have already been used, the AdjustLayout() call below might have // altered them (MultiSalLayout::ImplAdjustMultiLayout() drops glyphs that need // fallback from the base layout, but then GenericSalLayout::LayoutText() // would not know to call SetNeedFallback()).
assert(glyphsImpl->GetFlags() & SalLayoutFlags::GlyphItemsOnly);
}
} #endif
if (nDrawOriginCluster.has_value())
{
aLayoutArgs.mnDrawOriginCluster = *nDrawOriginCluster;
}
if (nDrawMinCharPos.has_value())
{
aLayoutArgs.mnDrawMinCharPos = *nDrawMinCharPos;
}
if (nDrawEndCharPos.has_value())
{
aLayoutArgs.mnDrawEndCharPos = *nDrawEndCharPos;
}
double nEndGlyphCoord(0); if (!pDXArray.empty() || !pKashidaArray.empty())
{ // The provided advance and kashida arrays are indexed relative to the first visible cluster auto nJustMinCluster = nDrawMinCharPos.value_or(nMinIndex); auto nJustLen = nLen; if (nDrawEndCharPos.has_value())
{
nJustLen = *nDrawEndCharPos - nJustMinCluster;
}
if (!pDXArray.empty() && mbMap)
{ // convert from logical units to font units without rounding, // keeping accuracy for lower levels for (int i = 0; i < nJustLen; ++i)
{
stJustification.SetTotalAdvance(
nJustMinCluster + i,
ImplLogicWidthToDeviceSubPixel(pDXArray[i]));
}
nEndGlyphCoord = stJustification.GetTotalAdvance(nJustMinCluster + nJustLen - 1);
} elseif (!pDXArray.empty())
{ for (int i = 0; i < nJustLen; ++i)
{
stJustification.SetTotalAdvance(nJustMinCluster + i, pDXArray[i]);
}
if (!pKashidaArray.empty())
{ for (sal_Int32 i = 0; i < static_cast<sal_Int32>(pKashidaArray.size()); ++i)
{
stJustification.SetKashidaPosition(nJustMinCluster + i, static_cast<bool>(pKashidaArray[i]));
}
}
// get matching layout object for base font
std::unique_ptr<SalLayout> pSalLayout = mpGraphics->GetTextLayout(0);
if (pSalLayout)
{ constbool bActivateSubpixelPositioning(IsMapModeEnabled() || isSubpixelPositioning()); // tdf#168002 // SubpixelPositioning was until now activated when *any* MapMode was set, but // there is another case this is needed: When a TextSimplePortionPrimitive2D // is rendered by a SDPR. // In that case a TextLayouterDevice is used (to isolate all Text-related stuff // that should not be at OutputDevice) combined with a 'empty' OutDev -> no // MapMode used. It now gets SubpixelPositioning at it's OutDev to allow // checking/usage here. // The DXArray for Primitives (see that TextPrimitive) is defined in the // Unit-Text_Coordinate-System, thus in (0..1) ranges. That allows to // have the DXArray transformation-independent and thus re-usable and // is used since the TextPrimitive was created. // If there is a DXArray missing at the text Primitive (as is the case // with SVG imported ones, but allowed in general) one gets automatically // created during the SalLayout creation for rendering. Unfortunately there // (see GenericSalLayout::LayoutText, usages of GetSubpixelPositioning) the // coordinates get std::round'ed, so all up to that point correctly // calculated metric information in that double-precision dependent coordinate // space gets *shredded*. // To avoid that, SubpixelPositioning has to be activated. While this might // be done in the future for all cases (SubpixelPositioning == true) for now // just add this case with the Primitives to not break stuff.
pSalLayout->SetSubpixelPositioning(bActivateSubpixelPositioning);
}
// do glyph fallback if needed // #105768# avoid fallback for very small font sizes if (aLayoutArgs.HasFallbackRun() && mpFontInstance->GetFontSelectPattern().mnHeight >= 3)
pSalLayout = ImplGlyphFallbackLayout(std::move(pSalLayout), aLayoutArgs, pGlyphs);
if (flags & SalLayoutFlags::GlyphItemsOnly) // Return glyph items only after fallback handling. Otherwise they may // contain invalid glyph IDs. return pSalLayout;
// position, justify, etc. the layout
pSalLayout->AdjustLayout( aLayoutArgs );
// default to on for pdf export, which uses SubPixelToLogic to convert back to // the logical coord space, of if we are scaling/mapping if (mbMap || meOutDevType == OUTDEV_PDF)
pSalLayout->DrawBase() = ImplLogicToDeviceSubPixel(rLogicalPos); else
{
Point aDevicePos = ImplLogicToDevicePixel(rLogicalPos);
pSalLayout->DrawBase() = basegfx::B2DPoint(aDevicePos.X(), aDevicePos.Y());
}
// adjust to right alignment if necessary if( aLayoutArgs.mnFlags & SalLayoutFlags::RightAlign )
{ double nRTLOffset; if (!pDXArray.empty())
nRTLOffset = nEndGlyphCoord; elseif( nPixelWidth )
nRTLOffset = nPixelWidth; else
nRTLOffset = pSalLayout->GetTextWidth();
pSalLayout->DrawOffset().setX( 1 - nRTLOffset );
}
std::unique_ptr<SalLayout> pSalLayout = ImplLayout(
rStr, nIndex, nLen, Point(0, 0), 0, aKernArray, {}, eDefaultLayout, pLayoutCache, pGlyphs);
sal_Int32 nRetVal = -1; if( pSalLayout )
{ // convert logical widths into layout units // NOTE: be very careful to avoid rounding errors for nCharExtra case // problem with rounding errors especially for small nCharExtras // TODO: remove when layout units have subpixel granularity
tools::Long nSubPixelFactor = 1; if (!mbMap)
nSubPixelFactor = 64;
Color aOldTextColor;
Color aOldTextFillColor; bool bRestoreFillColor = false; if ( (nStyle & DrawTextFlags::Disable) && ! pVector )
{ bool bHighContrastBlack = false; bool bHighContrastWhite = false; const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() ); if( rStyleSettings.GetHighContrastMode() )
{
Color aCol; if( rTargetDevice.IsBackground() )
aCol = rTargetDevice.GetBackground().GetColor(); else // best guess is the face color here // but it may be totally wrong. the background color // was typically already reset
aCol = rStyleSettings.GetFaceColor();
// If there still is a last line, we output it left-aligned as the line would be clipped if ( !aLastLine.isEmpty() )
_rLayout.DrawText( aPos, aLastLine, 0, aLastLine.getLength(), pVector, pDisplayText );
if ( rOrigStr.isEmpty() || rRect.IsEmpty() ) return;
// we need a graphics if( !mpGraphics && !AcquireGraphics() ) return;
assert(mpGraphics); if( mbInitClipRegion )
InitClipRegion();
// temporarily swap in passed mtf for action generation, and // disable output generation. constbool bOutputEnabled( IsOutputEnabled() );
GDIMetaFile* pMtf = mpMetaFile;
mpMetaFile = &rMtf;
EnableOutput( false );
// #i47157# Factored out to ImplDrawTextRect(), to be shared // between us and DrawText()
vcl::DefaultTextLayout aLayout( *this );
ImplDrawText( *this, rRect, rOrigStr, nStyle, nullptr, nullptr, aLayout );
// and restore again
EnableOutput( bOutputEnabled );
mpMetaFile = pMtf;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.