Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/quartz/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 16 kB image not shown  

Quelle  salgdi.cxx   Sprache: C

 
/* -*- 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 .
 */


#include <sal/config.h>

#include <memory>

#include <sal/log.hxx>
#include <config_folders.h>

#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/range/b2drectangle.hxx>
#include <osl/diagnose.h>
#include <osl/file.hxx>
#include <osl/process.h>
#include <rtl/bootstrap.h>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>
#include <tools/long.hxx>
#include <comphelper/lok.hxx>

#include <vcl/metric.hxx>
#include <vcl/fontcharmap.hxx>
#include <vcl/svapp.hxx>
#include <vcl/sysdata.hxx>

#include <fontsubset.hxx>
#include <impfont.hxx>
#include <font/FontMetricData.hxx>
#include <font/fontsubstitution.hxx>
#include <font/PhysicalFontCollection.hxx>

#ifdef MACOSX
#include <osx/salframe.h>
#endif
#include <quartz/utils.h>
#ifdef IOS
#include <ios/iosinst.hxx>
#endif
#include <sallayout.hxx>

#include <config_features.h>
#include <vcl/skia/SkiaHelper.hxx>
#if HAVE_FEATURE_SKIA
#include <skia/osx/gdiimpl.hxx>
#endif

#include <quartz/SystemFontList.hxx>
#include <quartz/CoreTextFont.hxx>
#include <quartz/CoreTextFontFace.hxx>
#include <quartz/salgdi.h>

using namespace vcl;

namespace {

class CoreTextGlyphFallbackSubstititution
:    public vcl::font::GlyphFallbackFontSubstitution
{
public:
    bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString&) const override;
};

bool FontHasCharacter(CTFontRef pFont, const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen)
{
    auto const glyphs = std::make_unique<CGGlyph[]>(nLen);
    return CTFontGetGlyphsForCharacters(pFont, reinterpret_cast<const UniChar*>(rString.getStr() + nIndex), glyphs.get(), nLen);
}

}

bool CoreTextGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rPattern, LogicalFontInstance* pLogicalFont,
    OUString& rMissingChars) const
{
    bool bFound = false;
    CoreTextFont* pFont = static_cast<CoreTextFont*>(pLogicalFont);
    CFStringRef pStr = CreateCFString(rMissingChars);
    if (pStr)
    {
        CTFontRef pFallback = CTFontCreateForString(pFont->GetCTFont(), pStr, CFRangeMake(0, CFStringGetLength(pStr)));
        if (pFallback)
        {
            bFound = true;

            // tdf#148470 remove the resolved chars from rMissing to flag which ones are still missing
            // for an attempt with another font
            OUStringBuffer aStillMissingChars;
            for (sal_Int32 nStrIndex = 0; nStrIndex < rMissingChars.getLength();)
            {
                sal_Int32 nOldStrIndex = nStrIndex;
                rMissingChars.iterateCodePoints(&nStrIndex);
                sal_Int32 nCharLength = nStrIndex - nOldStrIndex;
                if (!FontHasCharacter(pFallback, rMissingChars, nOldStrIndex, nCharLength))
                    aStillMissingChars.append(rMissingChars.getStr() + nOldStrIndex, nCharLength);
            }
            rMissingChars = aStillMissingChars.toString();

            CTFontDescriptorRef pDesc = CTFontCopyFontDescriptor(pFallback);
            FontAttributes rAttr = DevFontFromCTFontDescriptor(pDesc, nullptr);

            rPattern.maSearchName = rAttr.GetFamilyName();

            CFRelease(pFallback);
            CFRelease(pDesc);
        }
        CFRelease(pStr);
    }

    return bFound;
}

AquaSalGraphics::AquaSalGraphics(bool bPrinter)
    : mnRealDPIX( 0 )
    , mnRealDPIY( 0 )
{
    SAL_INFO( "vcl.quartz""AquaSalGraphics::AquaSalGraphics() this=" << this );

#if HAVE_FEATURE_SKIA
    // tdf#146842 Do not use Skia for printing
    // Skia does not work with a native print graphics contexts. I am not sure
    // why but from what I can see, the Skia implementation drawing to a bitmap
    // buffer. However, in an NSPrintOperation, the print view's backing buffer
    // is CGPDFContext so even if this bug could be solved by blitting the
    // Skia bitmap buffer, the printed PDF would not have selectable text so
    // always disable Skia for print graphics contexts.
    if(!bPrinter && SkiaHelper::isVCLSkiaEnabled())
        mpBackend.reset(new AquaSkiaSalGraphicsImpl(*this, maShared));
    else
        mpBackend.reset(new AquaGraphicsBackend(maShared));
#else
    (void)bPrinter;
    mpBackend.reset(new AquaGraphicsBackend(maShared));
#endif

    for (int i = 0; i < MAX_FALLBACK; ++i)
        mpFont[i] = nullptr;

    if (comphelper::LibreOfficeKit::isActive())
        initWidgetDrawBackends(true);
}

AquaSalGraphics::~AquaSalGraphics()
{
    SAL_INFO( "vcl.quartz""AquaSalGraphics::~AquaSalGraphics() this=" << this );

    maShared.unsetClipPath();

    ReleaseFonts();

    maShared.mpXorEmulation.reset();

#ifdef IOS
    if (maShared.mbForeignContext)
        return;
#endif
    if (maShared.maLayer.isSet())
    {
        CGLayerRelease(maShared.maLayer.get());
    }
    else if (maShared.maContextHolder.isSet()
#ifdef MACOSX
             && maShared.mbWindow
#endif
             )
    {
        // destroy backbuffer bitmap context that we created ourself
        CGContextRelease(maShared.maContextHolder.get());
        maShared.maContextHolder.set(nullptr);
    }
}

SalGraphicsImpl* AquaSalGraphics::GetImpl() const
{
    return mpBackend->GetImpl();
}

void AquaSalGraphics::SetTextColor( Color nColor )
{
    maShared.maTextColor = nColor;
}

void AquaSalGraphics::GetFontMetric(FontMetricDataRef& rxFontMetric, int nFallbackLevel)
{
    if (nFallbackLevel < MAX_FALLBACK && mpFont[nFallbackLevel])
    {
        mpFont[nFallbackLevel]->GetFontMetric(rxFontMetric);
    }
}

static bool AddTempDevFont(const OUString& rFontFileURL)
{
    OUString aUSystemPath;
    OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFontFileURL, aUSystemPath ) );
    OString aCFileName = OUStringToOString( aUSystemPath, RTL_TEXTENCODING_UTF8 );

    CFStringRef rFontPath = CFStringCreateWithCString(nullptr, aCFileName.getStr(), kCFStringEncodingUTF8);
    CFURLRef rFontURL = CFURLCreateWithFileSystemPath(nullptr, rFontPath, kCFURLPOSIXPathStyle, true);

    CFErrorRef error;
    bool success = CTFontManagerRegisterFontsForURL(rFontURL, kCTFontManagerScopeProcess, &error);
    if (success)
    {
        // tdf155212 clear the cached system font list after loading a font
        // If the system font is not cached in SalData, loading embedded
        // fonts will be extremely slow and will trigger each frame and each
        // of its internal subframes to reload the system font list when
        // loading documents with embedded fonts.
        // So instead, reenable caching of the system font list in SalData
        // by reverting commit 3b6e9582ce43242a2304047561116bb26808408b.
        // Then, to prevent tdf#72456 from reoccurring, clear the cached
        // system font list after a font has been loaded or unloaded.
        // This should cause the first frame's request to reload the cached
        // system font list and all subsequent frames will avoid doing
        // duplicate font reloads.
        SalData* pSalData = GetSalData();
        pSalData->mpFontList.reset();
    }
    else
    {
        CFRelease(error);
    }
    CFRelease(rFontPath);
    CFRelease(rFontURL);

    return success;
}

static void AddTempFontDir( const OUString &rFontDirUrl )
{
    osl::Directory aFontDir( rFontDirUrl );
    osl::FileBase::RC rcOSL = aFontDir.open();
    if( rcOSL == osl::FileBase::E_None )
    {
        osl::DirectoryItem aDirItem;

        while( aFontDir.getNextItem( aDirItem, 10 ) == osl::FileBase::E_None )
        {
            osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
            rcOSL = aDirItem.getFileStatus( aFileStatus );
            if ( rcOSL == osl::FileBase::E_None )
            {
                AddTempDevFont(aFileStatus.getFileURL());
            }
        }
    }
}

static void AddLocalTempFontDirs()
{
    static bool bFirst = true;
    if( !bFirst )
        return;

    bFirst = false;

    // add private font files

    OUString aBrandStr( "$BRAND_BASE_DIR" );
    rtl_bootstrap_expandMacros( &aBrandStr.pData );

    // internal font resources, required for normal operation, like OpenSymbol
    AddTempFontDir( aBrandStr + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts/" );

    AddTempFontDir( aBrandStr + "/" LIBO_SHARE_FOLDER "/fonts/truetype/" );
}

void AquaSalGraphics::GetDevFontList(vcl::font::PhysicalFontCollection* pFontCollection)
{
    SAL_WARN_IF( !pFontCollection, "vcl""AquaSalGraphics::GetDevFontList(NULL) !");

    AddLocalTempFontDirs();

    // The idea is to cache the list of system fonts once it has been generated.
    // SalData seems to be a good place for this caching. However we have to
    // carefully make the access to the font list thread-safe. If we register
    // a font-change event handler to update the font list in case fonts have
    // changed on the system we have to lock access to the list. The right
    // way to do that is the solar mutex since GetDevFontList is protected
    // through it as should be all event handlers

    // Related tdf#155212: the system font list needs to be cached but that
    // should not cause tdf#72456 to reoccur now that the cached system font
    // is cleared immediately after a font has been loaded
    SalData* pSalData = GetSalData();
    if( !pSalData->mpFontList )
        pSalData->mpFontList = GetCoretextFontList();

    // Copy all PhysicalFontFace objects contained in the SystemFontList
    pSalData->mpFontList->AnnounceFonts( *pFontCollection );

    static CoreTextGlyphFallbackSubstititution aSubstFallback;
    pFontCollection->SetFallbackHook(&aSubstFallback);
}

void AquaSalGraphics::ClearDevFontCache()
{
    SalData* pSalData = GetSalData();
    pSalData->mpFontList.reset();
}

bool AquaSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection*,
    const OUString& rFontFileURL, const OUString& /*rFontName*/)
{
    return ::AddTempDevFont(rFontFileURL);
}

void AquaSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
{
    mpBackend->drawTextLayout(rLayout);
}

#ifdef MACOSX

bool AquaSalGraphics::ShouldDownscaleIconsAtSurface(double& rScaleOut) const
{
    if (comphelper::LibreOfficeKit::isActive())
        return SalGraphics::ShouldDownscaleIconsAtSurface(rScaleOut);
    rScaleOut = sal::aqua::getWindowScaling();
    return true;
}

#endif

void AquaGraphicsBackend::drawTextLayout(const GenericSalLayout& rLayout)
{
#ifdef IOS
    if (!mrShared.checkContext())
    {
        SAL_WARN("vcl.quartz""AquaSalGraphics::DrawTextLayout() without context");
        return;
    }
#endif

    const CoreTextFont& rFont = *static_cast<const CoreTextFont*>(&rLayout.GetFont());
    const vcl::font::FontSelectPattern& rFontSelect = rFont.GetFontSelectPattern();
    if (rFontSelect.mnHeight == 0)
    {
        SAL_WARN("vcl.quartz""AquaSalGraphics::DrawTextLayout(): rFontSelect.mnHeight is zero!?");
        return;
    }

    CTFontRef pCTFont = rFont.GetCTFont();
    CGAffineTransform aRotMatrix = CGAffineTransformMakeRotation(-rFont.mfFontRotation);

    basegfx::B2DPoint aPos;
    const GlyphItem* pGlyph;
    std::vector<CGGlyph> aGlyphIds;
    std::vector<CGPoint> aGlyphPos;
    std::vector<bool> aGlyphOrientation;
    int nStart = 0;
    while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
    {
        CGPoint aGCPos = CGPointMake(aPos.getX(), -aPos.getY());

        // Whether the glyph should be upright in vertical mode or not
        bool bUprightGlyph = false;

        if (rFont.mfFontRotation)
        {
            if (pGlyph->IsVertical())
                bUprightGlyph = true;
            else
                // Transform the position of rotated glyphs.
                aGCPos = CGPointApplyAffineTransform(aGCPos, aRotMatrix);
        }

        aGlyphIds.push_back(pGlyph->glyphId());
        aGlyphPos.push_back(aGCPos);
        aGlyphOrientation.push_back(bUprightGlyph);
    }

    if (aGlyphIds.empty())
        return;

    assert(aGlyphIds.size() == aGlyphPos.size());
#if 0
    std::cerr << "aGlyphIds:[";
    for (unsigned i = 0; i < aGlyphIds.size(); i++)
    {
        if (i > 0)
            std::cerr << ",";
        std::cerr << aGlyphIds[i];
    }
    std::cerr << "]\n";
    std::cerr << "aGlyphPos:[";
    for (unsigned i = 0; i < aGlyphPos.size(); i++)
    {
        if (i > 0)
            std::cerr << ",";
        std::cerr << aGlyphPos[i];
    }
    std::cerr << "]\n";
#endif

    mrShared.maContextHolder.saveState();
    RGBAColor textColor( mrShared.maTextColor );

    // The view is vertically flipped (no idea why), flip it back.
    CGContextScaleCTM(mrShared.maContextHolder.get(), 1.0, -1.0);
    CGContextSetShouldAntialias(mrShared.maContextHolder.get(), !mrShared.mbNonAntialiasedText);
    CGContextSetFillColor(mrShared.maContextHolder.get(), textColor.AsArray());

    if (rFont.NeedsArtificialBold())
    {

        float fSize = rFontSelect.mnHeight / 23.0f;
        CGContextSetStrokeColor(mrShared.maContextHolder.get(), textColor.AsArray());
        CGContextSetLineWidth(mrShared.maContextHolder.get(), fSize);
        CGContextSetTextDrawingMode(mrShared.maContextHolder.get(), kCGTextFillStroke);
    }

    if (rLayout.GetSubpixelPositioning())
    {
        CGContextSetAllowsFontSubpixelQuantization(mrShared.maContextHolder.get(), false);
        CGContextSetShouldSubpixelQuantizeFonts(mrShared.maContextHolder.get(), false);
        CGContextSetAllowsFontSubpixelPositioning(mrShared.maContextHolder.get(), true);
        CGContextSetShouldSubpixelPositionFonts(mrShared.maContextHolder.get(), true);
    }

    auto aIt = aGlyphOrientation.cbegin();
    while (aIt != aGlyphOrientation.cend())
    {
        bool bUprightGlyph = *aIt;
        // Find the boundary of the run of glyphs with the same rotation, to be
        // drawn together.
        auto aNext = std::find(aIt, aGlyphOrientation.cend(), !bUprightGlyph);
        size_t nStartIndex = std::distance(aGlyphOrientation.cbegin(), aIt);
        size_t nLen = std::distance(aIt, aNext);

        mrShared.maContextHolder.saveState();
        if (rFont.mfFontRotation && !bUprightGlyph)
        {
            CGContextRotateCTM(mrShared.maContextHolder.get(), rFont.mfFontRotation);
        }
        CTFontDrawGlyphs(pCTFont, &aGlyphIds[nStartIndex], &aGlyphPos[nStartIndex], nLen, mrShared.maContextHolder.get());
        mrShared.maContextHolder.restoreState();

        aIt = aNext;
    }

    mrShared.maContextHolder.restoreState();
}

void AquaSalGraphics::SetFont(LogicalFontInstance* pReqFont, int nFallbackLevel)
{
    // release the font
    for (int i = nFallbackLevel; i < MAX_FALLBACK; ++i)
    {
        if (!mpFont[i])
            break;
        mpFont[i].clear();
    }

    if (!pReqFont)
        return;

    // update the font
    mpFont[nFallbackLevel] = static_cast<CoreTextFont*>(pReqFont);
}

std::unique_ptr<GenericSalLayout> AquaSalGraphics::GetTextLayout(int nFallbackLevel)
{
    assert(mpFont[nFallbackLevel]);
    if (!mpFont[nFallbackLevel])
        return nullptr;
    return std::make_unique<GenericSalLayout>(*mpFont[nFallbackLevel]);
}

FontCharMapRef AquaSalGraphics::GetFontCharMap() const
{
    if (!mpFont[0])
    {
        return FontCharMapRef( new FontCharMap() );
    }

    return mpFont[0]->GetFontFace()->GetFontCharMap();
}

bool AquaSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
{
    if (!mpFont[0])
        return false;

    return mpFont[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
}

void AquaSalGraphics::Flush()
{
    mpBackend->Flush();
}

void AquaSalGraphics::Flush( const tools::Rectangle& rRect )
{
    mpBackend->Flush( rRect );
}

void AquaSalGraphics::WindowBackingPropertiesChanged()
{
    mpBackend->WindowBackingPropertiesChanged();
}

#ifdef IOS

bool AquaSharedAttributes::checkContext()
{
    if (mbForeignContext)
    {
        SAL_INFO("vcl.ios""CheckContext() this=" << this << ", mbForeignContext, return true");
        return true;
    }

    SAL_INFO( "vcl.ios""CheckContext() this=" << this << ", not foreign, return false");
    return false;
}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=88 G=91

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.