Blocksatz mit Printer()->Canvas



  • Ich möchte Textausgaben auf einem Drucker mit Printer()->Canvas->TextOut() im Blocksatz ausgeben. Kennt jemand eine Möglichkeit, um eine solche Ausgabe zu erzeugen? Vielleicht gibt es eine Komponente auf dem Markt?

    Vielen Dank für eure Hilfe!



  • Hallo!

    Ich würde das so angehen:
    Ersteinmal die Breite für den Blocksatz festlegen.
    Ne Schriftart auswählen.

    Dann mittels Printer()->Canvas->TextWidth("blahblah") sehen, wie breit der String ist - und ggf. mit Leerzeichen auffüllen (bevorzugt dort, wo schon Leerzeichen sind) - und wenn die Breite annähernd passt, druckst dus aufs Papier.

    Und das machst du dann für alle Strings, die du hast.

    ...oder du nimmst ne Report-Komponente, die das alles schon kann...

    tschüss
    Robert



  • Hier ein Ansatz einmal unter Verwendung der WinAPI-Funktion SetTextJustification() und einmal ganz ohne API, also ggf. auch unter Linux/Kylix verwendbar.
    'Blocksatz' wird im Englischen übrigens einfach als justified oder fully justified bezeichnet.

    //---------------------------------------------------------------------------
    void __fastcall TForm1::JustifiedTextOut(TCanvas *canv, TRect rect, String text)
    {
      String line = "";
      int line_nr = 0; // Zeilennummer (der Ausgabe)
      int line_spacing = 2; // Zeilenabstand in Pixeln
      int space_count = 0; // Leerzeichen mitzählen für SetTextJustification
    
      // Liste der einzelnen Wörter erstellen, ein einfaches Array würde es
      // auch tun, aber TStringList ist ja so bequem :-)
      TStringList *list = new TStringList();
      list->Delimiter = ' ';
      list->DelimitedText = text;
    
      int X = rect.Left;
      int Y = rect.Top;
    
      int indx = 0; // Index in der Wortliste
      while (indx < list->Count)
      {
        // Zeile mit Worten füllen, bis Breite der Zeichenfläche erreicht
        while (canv->TextWidth(line) <= rect.Width())
        {
          if (indx < list->Count)
          {
            line += list->Strings[indx++] + " ";
            space_count++;
          }
          else
          {
            space_count = 0;
            break;
          }
        }
    
        // wenn Breite überschritten, letztes Leerzeichen und ggf. letztes Wort löschen
        while (canv->TextWidth(line) > rect.Width())
        {
          // Leerzeichen am Ende löschen
          int pos = line.LastDelimiter(" ");
          line.Delete(pos, 1);
          space_count--; // ist durch das Löschen natürlich eins weniger
    
          // mit etwas Glück passt es jetzt ...
          if (canv->TextWidth(line) <= rect.Width())
            break;
          else // ... ansonsten muss halt auch das letzte Wort der Zeile dran
          {    // glauben, einschliesslich des davor stehenden Leerzeichens
            pos = line.LastDelimiter(" ");
            line.Delete(pos, line.Length() - pos + 1);
            indx--; // das gelöschte Wort soll ja in die nächste Zeile
            space_count--; // ist noch eins weniger
          }
        }
    
        // ermitteln des normalerweise frei bleibenden Raumes am Ende der Zeile
        int extraspace = rect.Width() - canv->TextWidth(line);
    
    #ifdef __linux__
    //Linux- bzw. non-API-Variante
    
        // Zeile in einzelne Worte zerlegen
        TStringList *tmplist = new TStringList();
        tmplist->Delimiter = ' ';
        tmplist->DelimitedText = line;
    
        // freien Platz gleichmässig auf die Zwischenräume (Leerzeichen) aufteilen
        int space_width = canv->TextWidth(" ");
    
        if (space_count > 1)
          space_width += (extraspace / space_count);
    
        // erstes Wort am Zeilenanfang ausgeben ...
        canv->TextOut(X, Y, tmplist->Strings[0]);
    
        // dann die restlichen Worte ...
        for (int i = 1; i < tmplist->Count; i++)
        {
          // ... jeweils versetzt um die Breite des  vorigen Wortes plus
          // der Breite des (ggf. erweiterten) Leerzeichens
          X = X + canv->TextWidth(tmplist->Strings[i-1]) + space_width;
    
          // bei unrunden oder kurzen freien Zeilenenden zumindest die ersten Zwischen-
          // räume um jeweils 1 verbreitern, soweit die Pixelzahl des Endes reicht
          if (space_count > 1 && i < extraspace % space_count)
            X++;
    
          // an neu ermittelter Position ausgeben
          canv->TextOut(X, Y, tmplist->Strings[i]);
        }
        delete tmplist;
        X = rect.Left;
    
    #else
    // WinAPI-Version
    
        SetTextJustification(canv->Handle, extraspace, space_count);
        canv->TextOut(X, Y, line);
        SetTextJustification(canv->Handle, 0, 0);
    #endif
    
        // für nächste Zeile vorbereiten
        line_nr++;
        Y = rect.Top + (line_nr * canv->TextHeight(line)) + line_spacing;
        space_count = 0;
        line = "";
      }
    
      delete list;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      // freizulassende Ränder definieren
      TRect rect(10, 10, Image1->Canvas->ClipRect.Right - 20, Image1->Canvas->ClipRect.Bottom - 20);
    
      JustifiedTextOut(Image1->Canvas, rect, text_to_draw);
    }
    //---------------------------------------------------------------------------
    

    Wie gesagt, das ist nur ein Ansatz, da fehlt z.B. das Prüfen auf echte Zeilenumbrüche, Leerzeilen usw.
    Na und etwas eleganter kann man das Ganze sicher auch noch gestalten.



  • Code aktualisiert.


Anmelden zum Antworten