How to: Disegna contorni icona TrueType

Traduzione articoli Traduzione articoli
Identificativo articolo: 243285 - Visualizza i prodotti a cui si riferisce l?articolo.
Espandi tutto | Chiudi tutto

Sommario

La funzione API Win32 GetGlyphOutline restituita icona nativo strutturare i dati da un tipo di carattere TrueType. Per disegnare una struttura di glifo TrueType, i dati devono essere convertiti dalla relativa definizione B-spline nativo a una sequenza di definizioni di riga di BÚzier. Quindi la funzione di API Win32 di PolyBezier pu˛ essere utilizzata per disegnare la struttura.

Informazioni

La funzione di GetGlyphOutline nella programmazione Interface(API) Win32 applicazioni Ŕ possibile recuperare una struttura di TrueType. L'opzione di formattazione GGO_NATIVE riempie un buffer con curve B-spline Quadratic per una struttura di TrueType. B-spline quadratiche vengono utilizzati da TrueType per descrivere la struttura di icona in un file di carattere TrueType. Per disegnare queste curve struttura una possibile implementare una funzione di disegno B-spline oppure utilizzare la funzione PolyBezier dall'API Win32.

Quadratiche curve B-spline sono una classe di curve parametrica che consente di definire il percorso di pi¨ segmenti della curva tramite alcuni punti di controllo. Una curva Quadratic Ŕ una seconda curva parametrica ordine. Non vi Ŕ alcuna funzione nell'API per disegnare una Spline Quadratic direttamente ma se il Quadratic viene convertito in un cubica con l'API Win32 possono essere disegnato funzione per disegnare una curva di BÚzier, chiamato PolyBezier .

In generale, quadratiche curve B-spline in particolare e parametrica curve sono un argomento well-researched di elementi grafici in informatica. Possono inoltre essere molto complesse. Gli algoritmi sono stati pubblicati in vari testi che Ŕ possibile utilizzare per implementare una funzione per disegnare una Spline Quadratic, ma che descrive un algoritmo di tali esula dall'ambito di questo articolo.

La funzione PolyBezier possibile disegnare una Spline Quadratic perchÚ una curva di BÚzier Ŕ una curva parametrica cubica o terza nell'ordine. PoichÚ una Spline Quadratic Ŕ un'equazione ordine seconda, pu˛ essere espresso in termini dell'equazione di cubi di ordine superiore. Sebbene un'equazione per esprimere un quadratic come un cubica viene specificata nel codice di esempio, la derivazione Ŕ descritto non in questo articolo.

Il codice di esempio in questo articolo Ŕ una dimostrazione di come analizzare un buffer di glifi GGO_NATIVE restituito dalla funzione GetGlyphOutline . Buffer restituito dal flag di formato GGO_NATIVE Ŕ conforme alla struttura TTPOLYGONHEADER . La struttura TTPOLYGONHEADER e i dati che segue immediatamente costituiscono una distribuzione di un glifo TrueType. Un profilo Ŕ un percorso completo di curve viene chiuso in modo implicito se non viene restituito in modo esplicito in questo modo.

Vedere la documentazione Platform SDK nelle strutture TTPOLYGONHEADER e TTPOLYCURVE .

Una distribuzione di glifo Ŕ costituito da pi¨ segmenti di curva rappresentati da strutture TTPOLYCURVE . In una distribuzione di TTPOLYGONHEADER Ŕ seguito da uno o pi¨ strutture TTPOLYCURVE e dati di coordinate del punto. Il membro pfxStart fornisce il punto di coordinata iniziale della distribuzione. Il numero di record di curva che seguono il TTPOLYGONHEADER Ŕ determinato dal membro cb . Il punto di partenza Ŕ relativa all'origine dell'icona. L'origine di un glifo Ŕ l'angolo inferiore sinistro del carattere alla linea di base del carattere.

Ogni record di curva TTPOLYCURVE (un segmento di curva) Ŕ costituito da punti di controllo B-spline Quadratic o semplici punti di polilinea. Il numero di punti Ŕ determinato dal membro cpfx TTPOLYCURVE . Dal membro wType viene assegnato il tipo di curva di spline o polilinea. Matrice di punti delle coordinate seguire immediatamente la struttura. Il punto iniziale della curva viene assegnato dal membro apfx .

PoichÚ un glifo pu˛ contenere pi¨ di una distribuzione, il buffer contiene uno o pi¨ gruppi di TTPOLYGONHEADER strutture e dati. Distribuzioni successive vengono compressi immediatamente dopo quello precedente nel buffer nativo. La distribuzione successiva inizia con un TTPOLYGONHEADER dal byte successivo oltre l'ultimo punto del record precedente curva.

Questo codice di esempio consente di disegnare la struttura di un glifo TrueType analizzando il buffer GGO_NATIVE per creare un elenco di segmenti di linea BÚzier che costituiscono ogni distribuzione. Elenco di segmenti di Bezier riga viene quindi disegnato utilizzando PolyBezier . Il codice che analizza il buffer Ŕ la funzione DrawT2Outline .

Il primo passaggio per creare un elenco di righe di BÚzier consiste nel determinare la dimensione del buffer per l'elenco. Quattro punti definiscono le righe di BÚzier. La funzione PolyBezier interpreta una matrice di punti come una riga continua di segmenti di Bezier in cui l'inizio della riga successiva Ŕ che coincidono con l'endpoint della riga precedente. Di conseguenza, solo tre punti sono necessari per descrivere un segmento di linea BÚzier aggiuntivo.

Il buffer GGO_NATIVE pu˛ contenere una curva di polilinea o una curva B-spline Quadratic. Due punti definiscono un segmento di linea durante tre punti definiscono una B-spline. PoichÚ ogni tipo Ŕ essere disegnato con una linea di BÚzier utilizzando PolyBezier, nello scenario peggiore viene si una verifica quando una distribuzione che Ŕ costituita interamente da segmenti di linea si espande per Beziers pi¨.

Si noti che per esprimere una curva ulteriore segmento in una rappresentazione di curva B-spline richiede solo un punto supplementare. Tale punto definisce esplicitamente "B" o disattivare curva scegliere e in modo implicito definisce aggiuntiva nel punto della curva. Il punto di curva su Ŕ il punto centrale al successivo punto di "B". Pertanto o un segmento di linea aggiuntiva che un segmento di curva ulteriori si espanderÓ per tre ulteriori punti di una riga di BÚzier.

Nel codice si presuppone che l'intero buffer nativo sarÓ composto da POINTFX strutture in modo che Ŕ possibile determinare il maggior numero possibile di punti rappresentato da un buffer di. Il numero effettivo Ŕ leggermente inferiore poichÚ Ŕ presente un sovraccarico della struttura di descrivere le distribuzioni e le curve. La dimensione del buffer per l'elenco di BÚzier Ŕ determinata dal numero massimo di punti di moltiplicato per il dati dimensione di una struttura POINT e moltiplicato per tre. Tre Ŕ il numero di punti aggiuntivi necessari per ogni segmento di linea aggiuntiva o di un segmento di spline quadratica quando convertito in un tipo di BÚzier.

Una volta che Ŕ stato allocato il buffer di BÚzier il codice analizza il buffer nativo avviando nel primo TTPOLYGONHEADER . Durante un'iterazione del ciclo il codice elabora una distribuzione e disegno. Un secondo ciclo interno analizza ciascun tipo di curva.

In caso di una curva della polilinea (TT_PRIM_LINE), ogni segmento di linea della polilinea convertita viene aggiunto all'elenco di Beziers con la funzione AppendPolyLineToBezier . Se si utilizza la curva B-spline Quadratic (TT_PRIM_QSPLINE), la funzione AppendQuadBSplineToBezier converte e aggiunge Quadratic B-spline. Ciascuna di queste funzioni utilizza un ciclo per analizzare da ogni segmento della curva in relativi punti di controllo indipendenti. Quindi la riga o la spline viene convertito in un BÚzier e aggiunto all'elenco di Beziers.

Una riga di BÚzier possibile disegnare una linea retta facilmente. L'inizio e fine vettori posizionare semplicemente all'estremitÓ opposta del segmento di linea. Questa operazione viene eseguita nella funzione MakeBezierFromLine .

Per convertire una spline quadratica in una spline di BÚzier cubica, Ŕ necessario esprimere punti di controllo del cubica i in termini di punti di controllo del quadratic il. L'equazione per tradurre i punti di controllo si trova nella funzione MakeBezierFromQBSpline .

Prima di disegnare la distribuzione, il codice garantisce che il percorso sia chiuso chiamando la funzione CloseContour . La funzione viene chiamata solo se il punto di fine della sequenza di Beziers iniziale e non sono che coincidono. Quindi viene chiamata la funzione PolyBezier .

Dopo la distribuzione Ŕ disegnata, TTPOLYGONHEADER della distribuzione successivo Trova per far avanzare il puntatore lpHeader oltre la fine dei record del profilo corrente. Se questo dÓ come risultato un valore di puntatore oltre la fine del buffer nativo, il codice ha elaborato tutte le distribuzioni e la chiusura.
/****************************************************************************
 *  FUNCTION   : IntFromFixed
 *  RETURNS    : int value approximating the FIXED value.
 ****************************************************************************/ 
int PASCAL NEAR IntFromFixed(FIXED f)
{
    if (f.fract >= 0x8000)
    return(f.value + 1);
    else
    return(f.value);
}

/****************************************************************************
 *  FUNCTION   : fxDiv2
 *  RETURNS    : (val1 + val2)/2 for FIXED values
 ****************************************************************************/ 
FIXED PASCAL NEAR fxDiv2(FIXED fxVal1, FIXED fxVal2)
{
    long l;

    l = (*((long far *)&(fxVal1)) + *((long far *)&(fxVal2)))/2;
    return(*(FIXED *)&l);
}

/****************************************************************************
 *  FUNCTION   : MakeBezierFromLine
 *
 *  PURPOSE    : Converts a line define by two points to a four point Bezier
 *               spline representation of the line in pPts.
 *
 *
 *  RETURNS    : number of Bezier points placed into the pPts POINT array.
 ****************************************************************************/ 
UINT MakeBezierFromLine( POINT *pPts, POINT startpt, POINT endpt )
{
    UINT cTotal = 0;

    // starting point of Bezier
    pPts[cTotal] = startpt;
    cTotal++;

    // 1rst Control, pt == endpoint makes Bezier a line
    pPts[cTotal].x = endpt.x;
    pPts[cTotal].y = endpt.y;
    cTotal++;

    // 2nd Control, pt == startpoint makes Bezier a line
    pPts[cTotal].x = startpt.x;
    pPts[cTotal].y = startpt.y;
    cTotal++;

    // ending point of Bezier
    pPts[cTotal] = endpt;
    cTotal++;
    
    return cTotal;
}

/****************************************************************************
 *  FUNCTION   : MakeBezierFromQBSpline
 *
 *  PURPOSE    : Converts a quadratic spline in pSline to a four point Bezier
 *               spline in pPts.
 *
 *
 *  RETURNS    : number of Bezier points placed into the pPts POINT array.
 ****************************************************************************/ 
UINT MakeBezierFromQBSpline( POINT *pPts, POINTFX *pSpline )
{
    POINT   P0,         // Quadratic on curve start point
            P1,         // Quadratic control point
            P2;         // Quadratic on curve end point
    UINT    cTotal = 0;

    // Convert the Quadratic points to integer
    P0.x = IntFromFixed( pSpline[0].x );
    P0.y = IntFromFixed( pSpline[0].y );
    P1.x = IntFromFixed( pSpline[1].x );
    P1.y = IntFromFixed( pSpline[1].y );
    P2.x = IntFromFixed( pSpline[2].x );
    P2.y = IntFromFixed( pSpline[2].y );

    // conversion of a quadratic to a cubic

    // Cubic P0 is the on curve start point
    pPts[cTotal] = P0;
    cTotal++;
    
    // Cubic P1 in terms of Quadratic P0 and P1
    pPts[cTotal].x = P0.x + 2*(P1.x - P0.x)/3;
    pPts[cTotal].y = P0.y + 2*(P1.y - P0.y)/3;
    cTotal++;

    // Cubic P2 in terms of Qudartic P1 and P2
    pPts[cTotal].x = P1.x + 1*(P2.x - P1.x)/3;
    pPts[cTotal].y = P1.y + 1*(P2.y - P1.y)/3;
    cTotal++;

    // Cubic P3 is the on curve end point
    pPts[cTotal] = P2;
    cTotal++;

    return cTotal;
}


/****************************************************************************
 *  FUNCTION   : AppendPolyLineToBezier
 *
 *  PURPOSE    : Converts line segments into their Bezier point 
 *               representation and appends them to a list of Bezier points. 
 *
 *               WARNING - The array must have at least one valid
 *               start point prior to the address of the element passed.
 *
 *  RETURNS    : number of Bezier points added to the POINT array.
 ****************************************************************************/ 
UINT AppendPolyLineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
    int     i;
    UINT    cTotal = 0;
    POINT   endpt;
    POINT   startpt;
    POINT   bezier[4];

    endpt.x = IntFromFixed(start.x);
    endpt.y = IntFromFixed(start.y);

    for (i = 0; i < lpCurve->cpfx; i++)
    {
        // define the line segment
        startpt = endpt;
        endpt.x = IntFromFixed(lpCurve->apfx[i].x);
        endpt.y = IntFromFixed(lpCurve->apfx[i].y);

        // convert a line to a bezier representation
        MakeBezierFromLine( bezier, startpt, endpt );

        // append the Bezier to the existing ones
                                    // Point 0 is Point 3 of previous.
        pt[cTotal++] = bezier[1];   // Point 1
        pt[cTotal++] = bezier[2];   // Point 2
        pt[cTotal++] = bezier[3];   // Point 3

    }

    return cTotal;
}


/****************************************************************************
 *  FUNCTION   : AppendQuadBSplineToBezier
 *
 *  PURPOSE    : Converts Quadratic spline segments into their Bezier point 
 *               representation and appends them to a list of Bezier points. 
 *
 *               WARNING - The array must have at least one valid
 *               start point prior to the address of the element passed.
 *
 *  RETURNS    : number of Bezier points added to the POINT array.
 ****************************************************************************/ 
UINT AppendQuadBSplineToBezier( LPPOINT pt, POINTFX start, LPTTPOLYCURVE lpCurve )
{
    WORD                i;
    UINT                cTotal = 0;
    POINTFX             spline[3];  // a Quadratic is defined by 3 points
    POINT               bezier[4];  // a Cubic by 4

    // The initial A point is on the curve.
    spline[0] = start;

    for (i = 0; i < lpCurve->cpfx;)
    {
        // The B point.
        spline[1] = lpCurve->apfx[i++];

        // Calculate the C point.
        if (i == (lpCurve->cpfx - 1))
        {
            // The last C point is described explicitly
            // i.e. it is on the curve.
            spline[2] = lpCurve->apfx[i++];
        }     
        else
        {
            // C is midpoint between B and next B point
            // because that is the on curve point of 
            // a Quadratic B-Spline.
            spline[2].x = fxDiv2(
                lpCurve->apfx[i-1].x,
                lpCurve->apfx[i].x
                );
            spline[2].y = fxDiv2(
                lpCurve->apfx[i-1].y,
                lpCurve->apfx[i].y
                );
        }

        // convert the Q Spline to a Bezier
        MakeBezierFromQBSpline( bezier, spline );
        
        // append the Bezier to the existing ones
                                    // Point 0 is Point 3 of previous.
        pt[cTotal++] = bezier[1];   // Point 1
        pt[cTotal++] = bezier[2];   // Point 2
        pt[cTotal++] = bezier[3];   // Point 3

        // New A point for next slice of spline is the 
        // on curve C point of this B-Spline
        spline[0] = spline[2];
    }

    return cTotal;
}

/****************************************************************************
 *  FUNCTION   : CloseContour
 *
 *  PURPOSE    : Adds a bezier line to close the circuit defined in pt.
 *
 *
 *  RETURNS    : number of points aded to the pt POINT array.
 ****************************************************************************/ 
UINT CloseContour( LPPOINT pt, UINT cTotal )
{
    POINT               endpt, 
                        startpt;    // definition of a line
    POINT               bezier[4];

    // connect the first and last points by a line segment
    startpt = pt[cTotal-1];
    endpt = pt[0];

    // convert a line to a bezier representation
    MakeBezierFromLine( bezier, startpt, endpt );

    // append the Bezier to the existing ones
                                // Point 0 is Point 3 of previous.
    pt[cTotal++] = bezier[1];   // Point 1
    pt[cTotal++] = bezier[2];   // Point 2
    pt[cTotal++] = bezier[3];   // Point 3

    return 3;
}

/****************************************************************************
 *  FUNCTION   : DrawT2Outline
 *
 *  PURPOSE    : Decode the GGO_NATIVE outline, create a sequence of Beziers
 *               for each contour, draw with PolyBezier.  Color and relative 
 *               positioning provided by caller. The coordinates of hDC are
 *               assumed to have MM_TEXT orientation.
 *
 *               The outline data is not scaled. To draw a glyph unhinted
 *               the caller should create the font at its EMSquare size
 *               and retrieve the outline data. Then setup a mapping mode
 *               prior to calling this function.
 *
 *  RETURNS    : none.
 ****************************************************************************/ 
void DrawT2Outline(HDC hDC, LPTTPOLYGONHEADER lpHeader, DWORD size) 
{
    WORD                i;
    UINT                cTotal = 0; // Total points in a contour.
    LPTTPOLYGONHEADER   lpStart;    // the start of the buffer
    LPTTPOLYCURVE       lpCurve;    // the current curve of a contour
    LPPOINT             pt;         // the bezier buffer
    POINTFX             ptStart;    // The starting point of a curve
    DWORD               dwMaxPts = size/size of(POINTFX); // max possible pts.
    DWORD               dwBuffSize;

    dwBuffSize = dwMaxPts *     // Maximum possible # of contour points.
                 sizeof(POINT) * // sizeof buffer element
                 3;             // Worst case multiplier of one additional point
                                // of line expanding to three points of a bezier

   lpStart = lpHeader;
   pt = (LPPOINT)malloc( dwBuffSize );

    // Loop until we have processed the entire buffer of contours.
    // The buffer may contain one or more contours that begin with
    // a TTPOLYGONHEADER. We have them all when we the end of the buffer.
    while ((DWORD)lpHeader < (DWORD)(((LPSTR)lpStart) + size) && pt != NULL)
    {
        if (lpHeader->dwType == TT_POLYGON_TYPE)
        // Draw each coutour, currently this is the only valid
        // type of contour.
        {
            // Convert the starting point. It is an on curve point.
            // All other points are continuous from the "last" 
            // point of the contour. Thus the start point the next
            // bezier is always pt[cTotal-1] - the last point of the 
            // previous bezier. See PolyBezier.
            cTotal = 1;
            pt[0].x = IntFromFixed(lpHeader->pfxStart.x);
            pt[0].y = IntFromFixed(lpHeader->pfxStart.y);

            // Get to first curve of contour - 
            // it starts at the next byte beyond header
            lpCurve = (LPTTPOLYCURVE) (lpHeader + 1);

            // Walk this contour and process each curve( or line ) segment 
            // and add it to the Beziers
            while ((DWORD)lpCurve < (DWORD)(((LPSTR)lpHeader) + lpHeader->cb))
            {
                //**********************************************
                // Format assumption:
                //   The bytes immediately preceding a POLYCURVE
                //   structure contain a valid POINTFX.
                // 
                //   If this is first curve, this points to the 
                //      pfxStart of the POLYGONHEADER.
                //   Otherwise, this points to the last point of
                //      the previous POLYCURVE.
                // 
                //   In either case, this is representative of the
                //      previous curve's last point.
                //**********************************************

                ptStart = *(LPPOINTFX)((LPSTR)lpCurve - sizeof(POINTFX));
                if (lpCurve->wType == TT_PRIM_LINE)
                {
                    // convert the line segments to Bezier segments
                    cTotal += AppendPolyLineToBezier( &pt[cTotal], ptStart, lpCurve );
                    i = lpCurve->cpfx;
                }
                else if (lpCurve->wType == TT_PRIM_QSPLINE)
                {
                    // Decode each Quadratic B-Spline segment, convert to bezier,
                    // and append to the Bezier segments
                    cTotal += AppendQuadBSplineToBezier( &pt[cTotal], ptStart, lpCurve );
                    i = lpCurve->cpfx;
                }
                else
                    // Oops! A POLYCURVE format we don't understand.
                    ; // error, error, error

            // Move on to next curve in the contour.
            lpCurve = (LPTTPOLYCURVE)&(lpCurve->apfx[i]);
            }

            // Add points to close the contour.
            // All contours are implied closed by TrueType definition.
            // Depending on the specific font and glyph being used, these
            // may not always be needed.
            if ( pt[cTotal-1].x != pt[0].x || pt[cTotal-1].y != pt[0].y )
            {
                cTotal += CloseContour( pt, cTotal );
            }

            // flip coordinates to get glyph right side up (Windows coordinates)
            // TT native coordiantes are zero originate at lower-left.
            // Windows MM_TEXT are zero originate at upper-left.
            for (i = 0; i < cTotal; i++)
                pt[i].y = 0 - pt[i].y;

            // Draw the contour
            PolyBezier( hDC, pt, cTotal );
        }
        else
            // Bad, bail, must have a bogus buffer.
            break; // error, error, error

        // Move on to next Contour.
        // Its header starts immediate after this contour
        lpHeader = (LPTTPOLYGONHEADER)(((LPSTR)lpHeader) + lpHeader->cb);
    }

    free( pt );
}
				

Riferimenti

Per ulteriori informazioni sulla specifica TrueType vedere:
Microsoft TrueType Specifications (http://www.microsoft.com/typography/tt/tt.htm)

Disponibile anche nel Microsoft Developer Network Library CD in specifiche.

ProprietÓ

Identificativo articolo: 243285 - Ultima modifica: lunedý 12 febbraio 2007 - Revisione: 1.5
Le informazioni in questo articolo si applicano a:
  • Microsoft Win32 Application Programming Interface
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition
Chiavi:á
kbmt kbdswgdi2003swept kbdraw kbfont kbgdi kbhowto KB243285 KbMtit
Traduzione automatica articoli
Il presente articolo Ŕ stato tradotto tramite il software di traduzione automatica di Microsoft e non da una persona. Microsoft offre sia articoli tradotti da persone fisiche sia articoli tradotti automaticamente da un software, in modo da rendere disponibili tutti gli articoli presenti nella nostra Knowledge Base nella lingua madre dell?utente. Tuttavia, un articolo tradotto in modo automatico non Ŕ sempre perfetto. Potrebbe contenere errori di sintassi, di grammatica o di utilizzo dei vocaboli, pi¨ o meno allo stesso modo di come una persona straniera potrebbe commettere degli errori parlando una lingua che non Ŕ la sua. Microsoft non Ŕ responsabile di alcuna imprecisione, errore o danno cagionato da qualsiasi traduzione non corretta dei contenuti o dell?utilizzo degli stessi fatto dai propri clienti. Microsoft, inoltre, aggiorna frequentemente il software di traduzione automatica.
Clicca qui per visualizzare la versione originale in inglese dell?articolo: 243285
LE INFORMAZIONI CONTENUTE NELLA MICROSOFT KNOWLEDGE BASE SONO FORNITE SENZA GARANZIA DI ALCUN TIPO, IMPLICITA OD ESPLICITA, COMPRESA QUELLA RIGUARDO ALLA COMMERCIALIZZAZIONE E/O COMPATIBILITA' IN IMPIEGHI PARTICOLARI. L'UTENTE SI ASSUME L'INTERA RESPONSABILITA' PER L'UTILIZZO DI QUESTE INFORMAZIONI. IN NESSUN CASO MICROSOFT CORPORATION E I SUOI FORNITORI SI RENDONO RESPONSABILI PER DANNI DIRETTI, INDIRETTI O ACCIDENTALI CHE POSSANO PROVOCARE PERDITA DI DENARO O DI DATI, ANCHE SE MICROSOFT O I SUOI FORNITORI FOSSERO STATI AVVISATI. IL DOCUMENTO PUO' ESSERE COPIATO E DISTRIBUITO ALLE SEGUENTI CONDIZIONI: 1) IL TESTO DEVE ESSERE COPIATO INTEGRALMENTE E TUTTE LE PAGINE DEVONO ESSERE INCLUSE. 2) I PROGRAMMI SE PRESENTI, DEVONO ESSERE COPIATI SENZA MODIFICHE, 3) IL DOCUMENTO DEVE ESSERE DISTRIBUITO INTERAMENTE IN OGNI SUA PARTE. 4) IL DOCUMENTO NON PUO' ESSERE DISTRIBUITO A SCOPO DI LUCRO.

Invia suggerimenti

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com