Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: FAQ - MFC ::  Drucken - Drucken mit der MFC (Doc-View)     Zeige alle Beiträge auf einer Seite Auf Beitrag antworten
Autor Nachricht
Taelan
Mitglied

Benutzerprofil
Anmeldungsdatum: 12.09.2005
Beiträge: 628
Beitrag Taelan Mitglied 10:31:04 26.09.2005   Titel:   Drucken - Drucken mit der MFC (Doc-View)            Zitieren

Drucken und Druckvorschau in der MFC

(wie man ein Projekt erstellt muss ich hoffentlich nicht mehr erklären ;)
ihr müsst nur darauf achten die Druckerunterstützung zu aktvieren)

Für unser Beispiel wollen wir einige Rechtecke auf dem Drucker ausgeben die über mehrere Seiten hinweg gezeichnet werden.

Zuerst modifizieren wir die OnDraw() Funktion unseres Projekts so das sie ein Rechteck zeichnet.

Code:
void CPrintView::OnDraw(CDC* pDC)
{
   pDC->Rectangle(20,20,200,200);
}
Code:
void CPrintView::OnDraw(CDC* pDC)
{
pDC->Rectangle(20,20,200,200);
}
Code:
void CPrintView::OnDraw(CDC* pDC)
{
   pDC->Rectangle(20,20,200,200);
}


Damit habt ihr schon eine funktionierende Druckerunterstützung - Gratuliere

Aber wenn ihr das ausgedruckte mit dem auf dem Bildschirm vergleicht seht ihr das die Größenverhältnisse nicht stimmen. Das rechteck wird wesentlich kleiner ausgedruckt.

Skalieren

Der Grund warum das Rechteck kleiner ausgedruckt wird als es dargestellt wird ist der das die Punkte auf dem Drucker kleiner sind als auf dem Bildschirm. Hier sieht man auch die Auswirkungen des Standartmäßig vorgegebenen Abbildungsmodus "MM_TEXT".

Dieses Problem lösen wir mit einem anderen Abbildungsmodus den wir folgendermaßen angeben:
Code:
pDC->SetMapMode(MM_LOENGLISH);
pDC->Rectangle(20,-20,220,-220);
Code:
pDC->SetMapMode(MM_LOENGLISH);
pDC->Rectangle(20,-20,220,-220);
Code:
pDC->SetMapMode(MM_LOENGLISH);
pDC->Rectangle(20,-20,220,-220);

MM_LOENGLISH eignet sich besser zum arbeiten mit Grafiken da er als Maßeinheiten statt Punkt oder Pixel, ein hunderstel Inch verwendet.
Bei pDC->Rectangle(20,-20,220,-220); fällt dem aufmerksamen Beobachter auf das wir plötzlich mit negativen werten arbeiten. Das liegt daran das MM_LOENGLISH die Koordinaten ander interpretiert.
Normal: X-Koordinaten werden nach rechts größer, Y-Koordianten werden nach unten Größer
MM_LOENGLISH: X-Koordinaten werden nach rechts Größer, Y-Koordinaten werden nach OBEN größer.
Am Ende des Textes eine Tabelle mit den zur verfügung stehenden Modi

Code:
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
       ^
     +Y|          
       |          
 -X    |    +X    
<-------------->
       |      
       |      
     -Y|      
Code:
1
2
3
4
5
6
7
8
^
+Y|
|
-X | +X
<-------------->
|
|
-Y|
Code:
1
2
3
4
5
6
7
8
       ^
     +Y|          
       |          
 -X    |    +X    
<-------------->
       |      
       |      
     -Y|      


Mehrere Seiten ausdrucken

Zuerst brauchen wir eine Membervariable in unserem Document
Dazu erstellen wir wie gewohnt die Membervariable m_numRects
und initialisieren sie im Konstruktor des Documents:
Code:
CPrint1Doc::CPrint1Doc():m_numRects(7)
{
    // ZU ERLEDIGEN: Hier Code für One-Time-Konstruktion einfügen

}
Code:
CPrint1Doc::CPrint1Doc():m_numRects(7)
{
// ZU ERLEDIGEN: Hier Code für One-Time-Konstruktion einfügen

}
Code:
CPrint1Doc::CPrint1Doc():m_numRects(7)
{
    // ZU ERLEDIGEN: Hier Code für One-Time-Konstruktion einfügen

}

Nun modifizieren wir unser OnDraw() so das mehrere Rechtecke gezeichnet werden:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CPrint1View::OnDraw(CDC* pDC)
{
    CPrint1Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    pDC->SetMapMode(MM_LOENGLISH);

    CString s;
    s.Format("%d", pDoc->m_numRects);
    pDC->TextOut(300,-100,s);
    for(int x=0; x<pDoc->m_numRects; x++)
    {
        pDC->Rectangle(20,-(20+x*200),220,-(220+x*200));
    }
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CPrint1View::OnDraw(CDC* pDC)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDC->SetMapMode(MM_LOENGLISH);

CString s;
s.Format("%d", pDoc->m_numRects);
pDC->TextOut(300,-100,s);
for(int x=0; x<pDoc->m_numRects; x++)
{
pDC->Rectangle(20,-(20+x*200),220,-(220+x*200));
}
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CPrint1View::OnDraw(CDC* pDC)
{
    CPrint1Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    pDC->SetMapMode(MM_LOENGLISH);

    CString s;
    s.Format("%d", pDoc->m_numRects);
    pDC->TextOut(300,-100,s);
    for(int x=0; x<pDoc->m_numRects; x++)
    {
        pDC->Rectangle(20,-(20+x*200),220,-(220+x*200));
    }
}

Schauen wir uns jetzt die Seitenansicht an sehen wir dass das 6. Rechteck ber die Seite hinaus ragt, auf der 2. Seite aber nichts angezeigt wird.
Um das zu korrigieren müssen wir der MFC mitteilen wie viele Seiten ausgegeben werden sollen. Hierfür müssen wir die Funktion SetMaxPage() in der OnBeginPrinting() der Ansichtsklasse aufrufen.
Als nächstes müssen wir die Seitenhöhe kennen. Dies lässt sich mit einem Aufruf der Funktion GetDeviceCaps() ermitteln. Um die vertikale Auflösung abzufragen, übergeben wir als Argument die Konstante VERTRES. Wenn wir HORZRES übergeben würden, erhalten wir die horizontale Auflösung.

Es gibt 29 Konstanten die an GetDeviceCaps() übergeben werden können. U.a. NUMFONTS für die Anzahl der unterstützten Fonts und DRIVERVERSION für die Versionsnummer des Treibers. Eine vollständige Liste findet ihr in der MSDN



Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
// Vorher die Kommentarzeichen im Funktionskopf entfernen
void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
    CPrint1Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    int pageHeight    = pDC->GetDeviceCaps(VERTRES);
    int logPixelsY    = pDC->GetDeviceCaps(LOGPIXELSY);
    int rectHeight  = (int)(2.2 * logPixelsY);
    int numPages    = 7 * rectHeight / pageHeight + 1;

    pInfo->SetMaxPage(numPages);
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
// Vorher die Kommentarzeichen im Funktionskopf entfernen
void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

int pageHeight = pDC->GetDeviceCaps(VERTRES);
int logPixelsY = pDC->GetDeviceCaps(LOGPIXELSY);
int rectHeight = (int)(2.2 * logPixelsY);
int numPages = 7 * rectHeight / pageHeight + 1;

pInfo->SetMaxPage(numPages);
}
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
// Vorher die Kommentarzeichen im Funktionskopf entfernen
void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
    CPrint1Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    int pageHeight    = pDC->GetDeviceCaps(VERTRES);
    int logPixelsY    = pDC->GetDeviceCaps(LOGPIXELSY);
    int rectHeight  = (int)(2.2 * logPixelsY);
    int numPages    = 7 * rectHeight / pageHeight + 1;

    pInfo->SetMaxPage(numPages);
}


Erstellen wir das Program jetzt noch einmal und schauen uns die Seitenansicht an so sehen wir das wir zwar inzwischen mehrere Seiten drucken könnten, der Seitenumbruch aber offensichtlich nicht funktioniert.

Den Ursprung festlegen

Damit die 2. und alle nachfolgenden Seiten korrekt ausgegeben werden müssen sie der MFC mitteilen wo die Seitenanfänge liegen. Zur Zeit zeichnet die MFC die Seiten genau so wie es ihr in OnDraw() vorgegeben wird, und zwar alle 7 Rechtecke vom begin der Seite bis zum Ende. Um der MFC mitzuteilen wo der neue Seitenanfang leigen soll, müssen wir zuerst die OnPrepareDC()-Funktion der Ansichtsklasse überschreiben.

Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
    if(pDC->IsPrinting())
    {
        int pageHeight = pDC->GetDeviceCaps(VERTRES);
        int originY = pageHeight * (pInfo->m_nCurPage - 1);
        pDC->SetViewportOrg(0,-originY);
    }
    CView::OnPrepareDC(pDC, pInfo);
}
Code:
1
2
3
4
5
6
7
8
9
10
void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
if(pDC->IsPrinting())
{
int pageHeight = pDC->GetDeviceCaps(VERTRES);
int originY = pageHeight * (pInfo->m_nCurPage - 1);
pDC->SetViewportOrg(0,-originY);
}
CView::OnPrepareDC(pDC, pInfo);
}
Code:
1
2
3
4
5
6
7
8
9
10
void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
    if(pDC->IsPrinting())
    {
        int pageHeight = pDC->GetDeviceCaps(VERTRES);
        int originY = pageHeight * (pInfo->m_nCurPage - 1);
        pDC->SetViewportOrg(0,-originY);
    }
    CView::OnPrepareDC(pDC, pInfo);
}


Das MFC Framework ruft OnPrepareDC() auf, kurz bevor es die Daten auf dem Bildschirm oder Drucker ausgiebt. Für den Fall das OnPrepareDC() vor der Ausgabe der Daten auf dem Bildschirm aufgerufen wird werden wir an der Standartverarbeitung von OnPrepareDC() nichts ändern. Aus diesem Grund prüfen wir mithilfe von IsPrinting() ob die Anwendung die Daten drucken will.
Wenn ja müssen wir festlegen welche Daten auf die aktuelle seite gehören. Dazu benötigen wir die Höhe der Druckerseite in Punkten (GetDeviceCaps()).

Als Nächstes müssen wir für die Anzeige einen neuen Viewport-Ursprung(die Position der Koordianten 0,0) festlegen.

Wenn ihr das Programm nun ausführt seht ihr das die Seiten jetzt korrekt umgebrochen werden.


Anhang

Code:
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
Modus          |Einheit          |x                |y                |
---------------------------------------------------------------------|
MM_HIENGLISH   |0,001 Inch       |Vergrößert rechts|Vergrößert oben  |
MM_HIMETRIC    |0,01 Millimeter  |Vergrößert rechts|Vergrößert oben  |
MM_ISOTROPIC   |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_ANISOTROPIC |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_LOENGLISH   |0,01 Inch        |Vergrößert rechts|Vergrößert oben  |
MM_LOMETRIC    |0,1 Millimeter   |Vergrößert rechts|Vergrößert oben  |
MM_TEXT        |Gerätepixel      |Vergrößert rechts|Vergrößert unten |
MM_TWIPS       |1/1440 Inch      |Vergrößert rechts|Vergrößert oben  |
Code:
1
2
3
4
5
6
7
8
9
10
Modus |Einheit |x |y |
---------------------------------------------------------------------|
MM_HIENGLISH |0,001 Inch |Vergrößert rechts|Vergrößert oben |
MM_HIMETRIC |0,01 Millimeter |Vergrößert rechts|Vergrößert oben |
MM_ISOTROPIC |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_ANISOTROPIC |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_LOENGLISH |0,01 Inch |Vergrößert rechts|Vergrößert oben |
MM_LOMETRIC |0,1 Millimeter |Vergrößert rechts|Vergrößert oben |
MM_TEXT |Gerätepixel |Vergrößert rechts|Vergrößert unten |
MM_TWIPS |1/1440 Inch |Vergrößert rechts|Vergrößert oben |
Code:
1
2
3
4
5
6
7
8
9
10
Modus          |Einheit          |x                |y                |
---------------------------------------------------------------------|
MM_HIENGLISH   |0,001 Inch       |Vergrößert rechts|Vergrößert oben  |
MM_HIMETRIC    |0,01 Millimeter  |Vergrößert rechts|Vergrößert oben  |
MM_ISOTROPIC   |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_ANISOTROPIC |Benutzerdefiniert|Benutzerdefiniert|Benutzerdefiniert|
MM_LOENGLISH   |0,01 Inch        |Vergrößert rechts|Vergrößert oben  |
MM_LOMETRIC    |0,1 Millimeter   |Vergrößert rechts|Vergrößert oben  |
MM_TEXT        |Gerätepixel      |Vergrößert rechts|Vergrößert unten |
MM_TWIPS       |1/1440 Inch      |Vergrößert rechts|Vergrößert oben  |


Memberfunktionen und -variablen der Klasse CPrintInfo
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Klassenelement     | Beschreibung
-----------------------------------------------------------------------------
SetMaxPage()       |Setzt die maximale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
SetMinPage()       |Setzt die minimale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
GetFromPage()      |Ermittelt die Nummer der ersten Seite, die der Anwender
                   |ausdrucken möchte.
-----------------------------------------------------------------------------
GetMaxPage()       |Ermittelt die maximale Seitenzahl des Dokuments, die in
                   |OnPreparePrinting() geändert worden sein kann.
-----------------------------------------------------------------------------
GetMinPage()       |Ermittelt die minimale Seitenzahl des Dokuments, die in
                   |OnPreparePrinting() geändert worden sein kann.
-----------------------------------------------------------------------------
GetToPage()        |Ermittelt die Nummer der letzten Seite, die der Anwender
                   |ausdrucken möchte.
-----------------------------------------------------------------------------
m_bContinuePrinting|Steuert den Druckprozess. Wird das Flag FALSE gesetzt
                   |endet der Druckjob
-----------------------------------------------------------------------------
m_bDirect          |Zeigt an, ob das Dokument zurzeit gedruckt wird
-----------------------------------------------------------------------------
m_bPreview         |Zeigt an, ob das Dokument zurzeit in der Druckvorschau
                   |steht
-----------------------------------------------------------------------------
m_nCurPage         |enthält die aktuelle Nummer der Seite die gerade gedruckt
                   |wird
-----------------------------------------------------------------------------
m_nNumPreviewPages |Enthällt die Anzahl der Seiten(eine oder zwei) die in der
                   |Druckvorschau angezeigt werden.
-----------------------------------------------------------------------------
m_pPD              |Enthällt einen Zeiger auf das PrintDialog-Objekt des
                   |Druckjobs
-----------------------------------------------------------------------------
m_rectDraw         |Enthällt ein Rechteck, das den nutzbaren Bereich für die
                   |aktuelle Seite definiert
-----------------------------------------------------------------------------
m_strPageDesc      |Enthällt einen Formatstring für die Seitennummer.
-----------------------------------------------------------------------------
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Klassenelement | Beschreibung
-----------------------------------------------------------------------------
SetMaxPage() |Setzt die maximale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
SetMinPage() |Setzt die minimale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
GetFromPage() |Ermittelt die Nummer der ersten Seite, die der Anwender
|ausdrucken möchte.
-----------------------------------------------------------------------------
GetMaxPage() |Ermittelt die maximale Seitenzahl des Dokuments, die in
|OnPreparePrinting()geändert worden sein kann.
-----------------------------------------------------------------------------
GetMinPage() |Ermittelt die minimale Seitenzahl des Dokuments, die in
|OnPreparePrinting()geändert worden sein kann.
-----------------------------------------------------------------------------
GetToPage() |Ermittelt die Nummer der letzten Seite, die der Anwender
|ausdrucken möchte.
-----------------------------------------------------------------------------
m_bContinuePrinting|Steuert den Druckprozess. Wird das Flag FALSE gesetzt
|endet der Druckjob
-----------------------------------------------------------------------------
m_bDirect |Zeigt an, ob das Dokument zurzeit gedruckt wird
-----------------------------------------------------------------------------
m_bPreview |Zeigt an, ob das Dokument zurzeit in der Druckvorschau
|steht
-----------------------------------------------------------------------------
m_nCurPage |enthält die aktuelle Nummer der Seite die gerade gedruckt
|wird
-----------------------------------------------------------------------------
m_nNumPreviewPages |Enthällt die Anzahl der Seiten(eine oder zwei) die in der
|Druckvorschau angezeigt werden.
-----------------------------------------------------------------------------
m_pPD |Enthällt einen Zeiger auf das PrintDialog-Objekt des
|Druckjobs
-----------------------------------------------------------------------------
m_rectDraw |Enthällt ein Rechteck, das den nutzbaren Bereich für die
|aktuelle Seite definiert
-----------------------------------------------------------------------------
m_strPageDesc |Enthällt einen Formatstring für die Seitennummer.
-----------------------------------------------------------------------------
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Klassenelement     | Beschreibung
-----------------------------------------------------------------------------
SetMaxPage()       |Setzt die maximale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
SetMinPage()       |Setzt die minimale Seitenzahl des Dokuments
-----------------------------------------------------------------------------
GetFromPage()      |Ermittelt die Nummer der ersten Seite, die der Anwender
                   |ausdrucken möchte.
-----------------------------------------------------------------------------
GetMaxPage()       |Ermittelt die maximale Seitenzahl des Dokuments, die in
                   |OnPreparePrinting() geändert worden sein kann.
-----------------------------------------------------------------------------
GetMinPage()       |Ermittelt die minimale Seitenzahl des Dokuments, die in
                   |OnPreparePrinting() geändert worden sein kann.
-----------------------------------------------------------------------------
GetToPage()        |Ermittelt die Nummer der letzten Seite, die der Anwender
                   |ausdrucken möchte.
-----------------------------------------------------------------------------
m_bContinuePrinting|Steuert den Druckprozess. Wird das Flag FALSE gesetzt
                   |endet der Druckjob
-----------------------------------------------------------------------------
m_bDirect          |Zeigt an, ob das Dokument zurzeit gedruckt wird
-----------------------------------------------------------------------------
m_bPreview         |Zeigt an, ob das Dokument zurzeit in der Druckvorschau
                   |steht
-----------------------------------------------------------------------------
m_nCurPage         |enthält die aktuelle Nummer der Seite die gerade gedruckt
                   |wird
-----------------------------------------------------------------------------
m_nNumPreviewPages |Enthällt die Anzahl der Seiten(eine oder zwei) die in der
                   |Druckvorschau angezeigt werden.
-----------------------------------------------------------------------------
m_pPD              |Enthällt einen Zeiger auf das PrintDialog-Objekt des
                   |Druckjobs
-----------------------------------------------------------------------------
m_rectDraw         |Enthällt ein Rechteck, das den nutzbaren Bereich für die
                   |aktuelle Seite definiert
-----------------------------------------------------------------------------
m_strPageDesc      |Enthällt einen Formatstring für die Seitennummer.
-----------------------------------------------------------------------------


Zuletzt bearbeitet von estartu am 10:57:16 16.01.2006, insgesamt 4-mal bearbeitet
Werbeunterbrechung
C/C++ Forum :: FAQ - MFC ::  Drucken - Drucken mit der MFC (Doc-View)   Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum nicht antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.