Seite 2 von 2

Verfasst: 04.02.2007 14:49
von Thorsten1867
Heul! :cry:
Da programmiert man stundenlang, nur um festzustellen, dass es einen viel einfacheren Weg gibt. :wink:

Vielleicht sollte ich eine direkte Hotline für Editorgadgets zu dir legen. :mrgreen:

Nachdem du jetzt schon einige Dinge aus dem Ärmel gezaubert hast, probiere ich es doch mal mit einer Frage, die mich schon seit Ewigkeiten zur Verzweiflung treibt:

Projekt: Ich erzeuge aus dem Inhalt eines Editorgadget eine PDF-Ausgabe.
Problem: Die Textbreitenberechnung (automatischer Zeilenumbruch) ist äußerst ungenau (im Vergleich zu PDF), was manchmal zu unterschiedlichen Zeilenumbrüchen führt.
Frage 1: Gibt es eine Möglichkeit ein Editorgadget zu einem genaueren Zeilenumbruch zu bewegen.
Frage 2: Hast du Codeschnipsel, WinAPI-Befehle oder Ideen zum Erstellen eines eigenen Editorgadgets mit einer eigenen Breitenberechnung.

Für die Vorgehensweise der Berechnung gibt es einen C++ Code, aber das hilft mir nichts solange ich keine passendes Eingabefenster habe:

Code: Alles auswählen

/*----------------------------------------------------------------------
   GetCharDesignWidths:  Liefert die Zeichenbreiten der Schrift unskaliert
  ----------------------------------------------------------------------*/

UINT GetCharDesignWidths (HDC hdc, UINT uFirst, UINT uLast, int * piWidths)
{
     HFONT             hFont, hFontDesign ;
     LOGFONT           lf ;
     OUTLINETEXTMETRIC otm ;

     hFont = GetCurrentObject (hdc, OBJ_FONT) ;
     GetObject (hFont, sizeof (LOGFONT), &lf) ;

     // Outline-Textmetrik abfragen. Hier geht es ausschließlich um die Größe 
     // des vom Schriftdesigners verwendeten Koordinatensystems, der Geräte-
     // kontext spielt dabei keine Rolle
     otm.otmSize = sizeof (OUTLINETEXTMETRIC) ;
     GetOutlineTextMetrics (hdc, sizeof (OUTLINETEXTMETRIC), &otm) ;

     // Neue Schrift mit einer logischen Größe anlegen, die dem 
     // TrueType-intenen Koordinatensystem exakt entspricht
     lf.lfHeight = - (int) otm.otmEMSquare ;  // Zeichenhöhe, nicht Zeilenabstand!
     lf.lfWidth  = 0 ;
     hFontDesign = CreateFontIndirect (&lf) ;

     // Schrift in den Gerätekontext einsetzen und Zeichenbreiten ermitteln
     SaveDC (hdc) ;
     SetMapMode (hdc, MM_TEXT) ;
     SelectObject (hdc, hFontDesign) ;

     GetCharWidth (hdc, uFirst, uLast, piWidths) ;
     SelectObject (hdc, hFont) ;
     RestoreDC (hdc, -1) ;

     // Aufräumen
     DeleteObject (hFontDesign) ;

     return otm.otmEMSquare ;
}

/*---------------------------------------------------------------------------
   GetScaledWidths:  Liefert die Zeichenbreiten für die gewählte Schriftgröße
      als Gleitkommawerte
  ---------------------------------------------------------------------------*/

void GetScaledWidths (HDC hdc, double * pdWidths)
{
     double  dScale ;
     HFONT   hFont ;
     int     aiDesignWidths [LASTCHAR + 1] ;
     int     i ;
     LOGFONT lf ;
     UINT    uEMSquare ;

     // Outline-Zeichenbreiten abfragen
     uEMSquare = GetCharDesignWidths (hdc, 0, LASTCHAR, aiDesignWidths) ;

    // Abfrage der aktuell in den Kontext eingesetzten Schrift
     hFont = GetCurrentObject (hdc, OBJ_FONT) ;
     GetObject (hFont, sizeof (LOGFONT), &lf) ;

     // Zeichenbreiten entsprechend skalieren und als Gleitkomma-Array speichern
     dScale = (double) -lf.lfHeight / (double) uEMSquare ;

     for (i = 0 ; i <= LASTCHAR ; i++)
          pdWidths[i] = dScale * aiDesignWidths[i] ;
}

/*----------------------------------------------------------------
   GetTextExtentFloat:  Berechnet Textbreiten mit Gleitkommawerten
  ----------------------------------------------------------------*/

double GetTextExtentFloat (double * pdWidths, PTSTR psText, int iCount)
{
     double dWidth = 0 ;
     int    i ;

     for (i = 0 ; i < iCount ; i++)
          dWidth += pdWidths [psText[i]] ;

     return dWidth ;
}

/*--------------------------------------------------------------------------------------
   Justify mit Breitenberechnung über die Originaldaten der Schrift, ohne Rundungsfehler
  --------------------------------------------------------------------------------------*/

void Justify (HDC hdc, PTSTR pText, RECT * prc, int iAlign)
{
     double dWidth, adWidths[LASTCHAR + 1] ;
     int    xStart, yStart, cSpaceChars ;
     PTSTR  pBegin, pEnd ;
     SIZE   size ;

     // Array adWidths mit skalierten Gleitkomma-Zeichenbreiten besetzen
     GetScaledWidths (hdc, adWidths) ;

     yStart = prc->top ;
     do                            // für jede Textzeile
     {
          cSpaceChars = 0 ;        // bis jetzt 0 Leerzeichen

          while (*pText == ' ')    // führende Leerzeichen überspringen
               pText++ ; 
          pBegin = pText ;         // Zeiger auf den Zeilenanfang
          
          do                       // solange, bis die Zeile bearbeitet ist
          {
               pEnd = pText ;      // aktuelles Zeilenende (beim 1. Mal = Zeilenstart)

               // Suche nach dem ersten/nächsten Leerzeichen
               while (*pText != '\0' && *pText++ != ' ') ;

               if (*pText == '\0')
                    break ;

               // Leerzeichen gefunden. Wie viel Platz braucht die Zeile jetzt?
               cSpaceChars++ ;
               dWidth = GetTextExtentFloat (adWidths, pBegin, pText - pBegin - 1) ;
          }
          while (dWidth < (double) (prc->right - prc->left)) ;
          
          cSpaceChars-- ;               // dieses Leerzeichen nicht mitzählen
          
          while (*(pEnd - 1) == ' ')    // und weitere Leerzeichen am Zeilenende raus
          {
               pEnd-- ;
               cSpaceChars-- ;
          }

         // Textende erreicht oder keine Leerzeichen gefunden?
          if (*pText == '\0' || cSpaceChars <= 0)
               pEnd = pText ;

         // Platzbedarf erst jetzt über die Standardfunktion abfragen
          GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size) ;
          
          switch (iAlign)               // Ausrichtung == xStart
          {
          case IDM_ALIGN_LEFT:
               xStart = prc->left ;
               break ;

          case IDM_ALIGN_RIGHT:
               xStart = prc->right - size.cx ;
               break ;

          case IDM_ALIGN_CENTER:
               xStart = (prc->right + prc->left - size.cx) / 2 ;
               break ;
               
          case IDM_ALIGN_JUSTIFIED:
               if (*pText != '\0' && cSpaceChars > 0)
                    SetTextJustification (hdc, prc->right - prc->left - size.cx, cSpaceChars) ;
               xStart = prc->left ;
               break ;
          }
          // Ausgabe einer Zeile
          TextOut (hdc, xStart, yStart, pBegin, pEnd - pBegin) ;

          // Vorbereitungen für die nächste Zeile
          SetTextJustification (hdc, 0, 0) ;
          yStart += size.cy ;
          pText = pEnd ;
     }
     while (*pText && yStart < prc->bottom - size.cy) ;
}
Ich wäre für jeden Codeschnippsel und jede Anregung dankbar, bevor ich mich ans C++ lernen mache, um obigen Code nach PB zu übersetzen.

PS: Kompleter Code (C. Petzold): http://thorsten-hoeppner.de/download/Justify2.zip

Verfasst: 04.02.2007 15:22
von edel
Ohne Beispiel kann ich mir darunter jetzt nicht viel vorstellen.

>>aber das hilft mir nichts solange ich keine passendes Eingabefenster habe:

?

C brauchste dafuer nicht lernen , notfalls machste daraus eine Userlib.

Verfasst: 04.02.2007 16:07
von Thorsten1867
Dem Code liegt auch eine EXE bei, leider ist das Fenster nur RO und ich müsste Text eingeben, der dann korrekt umgebrochen wird, wie bei:

Code: Alles auswählen

eg = EditorGadget(#Gadget_Bem,17,190,598,41)
SendMessage_(eg, #EM_SETTARGETDEVICE, 0, 0)
SendMessage_(eg, #EM_SHOWSCROLLBAR, #SB_VERT, #False)
http://thorsten-hoeppner.de/download/Justify2.exe

Verfasst: 04.02.2007 16:45
von edel
Ahh, denke das ich es verstanden habe.

Ob das so mit dem EditorGadget geht, weiss ich nicht. Mir faellt da nur
EM_SETWORDBREAKPROC ein , ob und wie dir das hilft, keine Ahnung,
habe damit noch nie etwas gemacht.

Falls du selber etwas machen moechtest schau dir auf MSDN mal
die Seite Using Carets an.

Verfasst: 04.02.2007 17:14
von Andreas
TomS hat geschrieben:Das is ja cool. Danke sehr!!
Oder so etwas vielleicht ?

Code: Alles auswählen

Procedure GetCurrentWord(Gadget)
  Protected result$,selstart.l,start.l,len.l
  SendMessage_(GadgetID(Gadget),#EM_EXGETSEL,0,ch.CHARRANGE)
  start = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,0,CH\cpMin)
  len = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,#WB_RIGHTBREAK,CH\cpMin) - start
  *buffer = AllocateMemory(len)
  tr.TEXTRANGE
  tr\chrg\cpmin = start
  tr\chrg\cpmax = start+len
  tr\lpstrtext = *Buffer
  SendMessage_(GadgetID(Gadget),#EM_GETTEXTRANGE,0,tr)
  Result$ = PeekS(*Buffer)
  FreeMemory(*Buffer)
  MessageRequester(Result$,"Word under Cursor",0)
EndProcedure

If OpenWindow(0, 10, 10, 400, 300, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  If CreateGadgetList(WindowID(0))
    ButtonGadget(1, 10,200,120,24,"GetCurrentWord")
    EditorGadget(0, 8, 8, 306, 133)
    SetGadgetText(0,"Das ist Text zum testen ! Setz den Cursor über ein Wort.")
  EndIf
  Repeat
    EventID = WaitWindowEvent()
    Select EventID
    Case #PB_Event_CloseWindow
      Quit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
      Case 1
        GetCurrentWord(0)
      EndSelect
    EndSelect
  Until Quit = 1
EndIf
End


Verfasst: 04.02.2007 17:40
von ts-soft
<offtopic>
@Andreas
Wie sieht es aus mit AvEditor für PB4?
</offtopic>

Verfasst: 04.02.2007 17:56
von Andreas
ts-soft hat geschrieben:<offtopic>
@Andreas
Wie sieht es aus mit AvEditor für PB4?
</offtopic>
Noch nicht ganz fertig. Im Moment finde ich nicht die Zeit dazu, wird aber werden.

Verfasst: 04.02.2007 18:26
von ts-soft
Andreas hat geschrieben:Noch nicht ganz fertig. Im Moment finde ich nicht die Zeit dazu, wird aber werden.
Hört sich gut an :allright:

Verfasst: 04.02.2007 19:01
von TomS
Andreas hat geschrieben:
TomS hat geschrieben:Das is ja cool. Danke sehr!!
Oder so etwas vielleicht ?

Code: Alles auswählen

Procedure GetCurrentWord(Gadget)
  Protected result$,selstart.l,start.l,len.l
  SendMessage_(GadgetID(Gadget),#EM_EXGETSEL,0,ch.CHARRANGE)
  start = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,0,CH\cpMin)
  len = SendMessage_(GadgetID(Gadget),#EM_FINDWORDBREAK,#WB_RIGHTBREAK,CH\cpMin) - start
  *buffer = AllocateMemory(len)
  tr.TEXTRANGE
  tr\chrg\cpmin = start
  tr\chrg\cpmax = start+len
  tr\lpstrtext = *Buffer
  SendMessage_(GadgetID(Gadget),#EM_GETTEXTRANGE,0,tr)
  Result$ = PeekS(*Buffer)
  FreeMemory(*Buffer)
  MessageRequester(Result$,"Word under Cursor",0)
EndProcedure

If OpenWindow(0, 10, 10, 400, 300, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  If CreateGadgetList(WindowID(0))
    ButtonGadget(1, 10,200,120,24,"GetCurrentWord")
    EditorGadget(0, 8, 8, 306, 133)
    SetGadgetText(0,"Das ist Text zum testen ! Setz den Cursor über ein Wort.")
  EndIf
  Repeat
    EventID = WaitWindowEvent()
    Select EventID
    Case #PB_Event_CloseWindow
      Quit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
      Case 1
        GetCurrentWord(0)
      EndSelect
    EndSelect
  Until Quit = 1
EndIf
End

Man man man. Ihr pflastert mich hier mit Codes zu, ich weiß gar nicht welchen ich verwenden soll. :oops: Danke euch allen. :allright: