Wie zeichne ich gezoomten Text, ohne die effektive Textbreite zu ändern?

8

Ich habe Code, der benutzerdefinierte Zeichnungen erstellt. Im Grunde ist es ein Formular-Füll-Programm, das einen WYSIWYG-Editor hat. Mit dem Editor kann eine Zoomstufe eingestellt werden. Ich habe Probleme mit der Breite meiner Etiketten, die zu anderen Größen springen, als alles andere auf dem Formular.

Ein Beispiel für den Code, den ich verwende, um den Text auszugeben, ist unten. Ich bin mir ziemlich sicher, dass das Problem mit der Änderung der Schriftgröße zusammenhängt, die nicht mit der Skalierung anderer Dinge übereinstimmt. Die Zoomstufe muss sich ausreichend ändern, um die Schrift vor dem Ändern des Textes auf die nächste Größe zu bringen, auch wenn sich bei jeder Änderung alles andere auf dem Formular um einige Pixel bewegt.

Dies führt zu zwei verschiedenen Problemen - der Text kann entweder zu klein mit viel Leerraum aussehen, oder der Text wird zwei große sein und das nächste Steuerelement überlappen. Die Dinge sehen wirklich schlecht aus, wenn ich eine vollständige Textzeile habe. Ein Ein-Wort-Etikett ändert sich nicht genug, um Probleme zu verursachen.

Ich habe über die Begrenzung der Zoomstufen nachgedacht - jetzt habe ich einen Schieberegler in 1% Schritten. Aber ich kann nicht sehen, dass eine Reihe von Ebenen besser ist als jede andere. Meine Formulare haben mehrere Beschriftungen in verschiedenen Schriftgrößen, die zu unterschiedlichen Zeiten zwischen kürzer und länger springen.

Die MultDiv-Funktion rundet das Ergebnis ab. Ich könnte diesen Wert abschneiden, um sicherzustellen, dass ich immer kleiner oder länger bin, aber das sieht genauso schlecht aus, weil die Lücken bei diesen Zoomstufen viel größer aussehen.

Hinweise zum Code:

Dies ist derzeit in Delphi 7. Dies ist unser letztes Projekt, das nicht vorangetrieben wurde, daher sind Antworten zu neueren Versionen von Delphi willkommen.

Wenn wir das untersuchen, habe ich gesehen, dass eine ExtDrawText-Funktion existiert. Der Wechsel zu dieser Funktion schien jedoch keinen Unterschied zu machen.

Das Recht der Bounding Box ist auf 0 gesetzt und der Text wird ohne Clipping gezeichnet, da das Tool, mit dem wir die Formulardefinition erstellen, nicht die richtige Grenze des Texts verfolgt. Wir richten sie nur visuell an den richtigen Ort aus.

%Vor%

Bearbeiten:

Mit mghies Antwort hier ist meine modifizierte Test-App. Der Zoom-Code ist mit dem Setzen des MapMode weg. Die TextOut-Funktion scheint jedoch immer noch eine vollständige Schriftgröße auszuwählen. Nichts scheint sich für den Text geändert zu haben, außer dass ich die Höhe der Schrift nicht selbst berechnen muss - der Kartenmodus macht das für mich.

Ich fand diese Webseite "The GDI Coordinate Systems" , die sehr nützlich war, aber sie war es nicht Textgröße nicht adressieren.

Hier ist meine Test App. Die Größe wird geändert, wenn Sie die Größe des Formulars ändern und ein Raster gezeichnet wird, sodass Sie sehen können, wie das Ende des Textes umherspringt.

%Vor%     
Mark Elder 16.12.2009, 22:40
quelle

5 Antworten

9

Das grundlegende Problem besteht darin, dass Sie versuchen, den Text zu vergrößern, indem Sie seine Height ändern. Da die Windows-API ein Ganzzahlkoordinatensystem verwendet, sind nur bestimmte diskrete Schrifthöhen möglich. Wenn Sie zum Beispiel eine 20 Pixel hohe Schriftart mit einem Skalenwert von 100% haben, können Sie grundsätzlich nur Skalierungswerte festlegen, die Vielfache von 5% sind. Schlimmer noch, selbst mit TrueType-Schriften werden nicht alle von ihnen erfreuliche Ergebnisse liefern.

Windows hat seit Jahren eine Möglichkeit, damit umzugehen, was die VCL leider nicht umschließt (und die sie intern auch nicht wirklich nutzt) - Mapping-Modi. Windows NT führte Transformationen ein, aber SetMapMode() war bereits in 16-Bit-Windows IIRC verfügbar.

Indem Sie einen Modus wie MM_HIMETRIC oder MM_HIENGLISH einstellen (abhängig davon, ob Sie in Metern oder in Fortschritten messen), können Sie die Schrifthöhe und das Begrenzungsrechteck berechnen, und da die Pixel sehr klein sind, können Sie fein zoomen rein oder raus.

Indem Sie die Modi MM_ISOTROPIC oder MM_ANISOTROPIC OTOH einstellen, können Sie die gleichen Werte für die Schrifthöhe und das umgebende Rechteck verwenden und stattdessen die Transformationsmatrix zwischen Seitenraum und Geräteraum anpassen, wenn sich der Zoomwert ändert .

Die SynEdit-Komponentensuite verfügte über ein Druckvorschau-Steuerelement (in der Datei SynEditPrintPreview.pas), das den Zuordnungsmodus MM_ANISOTROPIC verwendete, um eine Vorschau des druckbaren Texts in verschiedenen Zoomstufen zu ermöglichen. Dies kann als Beispiel nützlich sein, wenn es immer noch in SynEdit ist oder wenn Sie die alten Versionen finden können.

Bearbeiten:

Zu Ihrer Bequemlichkeit eine kleine Demo, getestet mit Delphi 4 und Delphi 2009:

%Vor%

Zweiter Schnitt:

Ich dachte ein bisschen mehr darüber nach, und ich denke, dass das Skalieren des Benutzercodes für Ihr Problem tatsächlich die einzige Möglichkeit ist, dies zu implementieren.

Sehen wir uns das an einem Beispiel an. Wenn Sie eine Textzeile mit einer Breite von 500 Pixeln und einer Schrifthöhe von 20 Pixeln bei einem Zoomfaktor von 100% haben, müssten Sie die Zoomstufe auf 105% erhöhen, um eine Textzeile mit 525 × 21 zu erhalten Pixelgröße. Für alle Ganzzahl-Zoomstufen dazwischen hätten Sie eine Ganzzahlbreite und eine nicht ganzzahlige Höhe dieses Textes. Die Textausgabe funktioniert jedoch nicht auf diese Weise. Sie können die Breite einer Textzeile nicht festlegen und das System die Höhe berechnen lassen. Die einzige Möglichkeit besteht darin, die Schrifthöhe für 100% bis 104% Zoom auf 20 Pixel zu erhöhen, aber für 105% bis 109% Zoom eine Schriftart mit 21 Pixel Höhe und so weiter. Dann ist der Text für die meisten Zoom-Werte zu schmal. Oder stellen Sie die Schrifthöhe auf 21 Pixel ein, beginnend mit 103% Zoom, und lebe damit, dass der Text zu breit ist.

Aber mit ein wenig zusätzlicher Arbeit können Sie erreichen, dass die Textbreite für jeden Zoomschritt um 5 Pixel erhöht wird. Der ExtTextOut() API-Aufruf verwendet ein optionales Integer-Array mit Zeichenursprüngen als letzter Parameter. Die meisten Codebeispiele, die ich kenne, verwenden dies nicht, aber Sie könnten damit zusätzliche Pixel zwischen einigen Zeichen einfügen, um die Breite der Textzeile auf den gewünschten Wert zu strecken, oder Zeichen näher aneinander verschieben, um die Breite zu verkleinern. Es würde mehr oder weniger so gehen:

  • Berechnen Sie die Schrifthöhe für den Zoomwert. Wählen Sie eine Schriftart mit dieser Höhe im Gerätekontext.
  • Rufen Sie die API-Funktion GetTextExtentExPoint() auf, um ein Array zu berechnen von Standard-Zeichenpositionen. Der letzte gültige Wert sollte die Breite der gesamten Zeichenfolge sein.
  • Berechnen Sie einen Maßstabswert für diese Zeichenpositionen, indem Sie die beabsichtigte Breite durch die tatsächliche Textbreite dividieren.
  • Multiplizieren Sie alle Zeichenpositionen mit diesem Skalenwert und runden Sie sie auf die nächste ganze Zahl ab. Je nachdem, ob der Maßstabswert höher oder niedriger als 1,0 ist, werden entweder zusätzliche Pixel an strategischen Positionen eingefügt oder einige Zeichen näher zueinander verschoben.
  • Verwenden Sie das berechnete Array von Zeichenpositionen im Aufruf von ExtTextOut() .

Dies ist ungetestet und kann einige Fehler oder Versehen enthalten, aber hoffentlich erlaubt dies Ihnen, die Textbreite unabhängig von der Texthöhe stufenlos zu skalieren. Vielleicht ist es den Aufwand für Ihre Bewerbung wert?

    
mghie 16.12.2009, 23:11
quelle
2

Eine weitere Möglichkeit, mit der Skalierung von Schriftarten umzugehen, besteht darin, sie in ein In-Memory-Bitmap zu zeichnen und dann mit StretchBlt() auf die gewünschte Größe zu strecken Die gleiche Idee wie in der vorherigen Antwort, aber die Umsetzung ist klarer.

Basisschritte sind:

  1. Setzen Sie den MM_ISOTROPIC-Mapping-Modus mit SetMapMode()
  2. Definieren Sie Koordinatenzuordnungen mit SetWindowExtEx() und SetViewPortExtEx()
  3. Zeichnen Sie Linien und Grafiken
  4. Wiederherstellungsabbildungsmodus
  5. Erstellen Sie eine Bitmap mit Originalgröße
  6. Zeichnen Sie Text in Bitmap
  7. Erstellen Sie eine transparente Bitmap mit der gewünschten Größe
  8. Kopieren Sie den Inhalt einer Bitmap mit Text in eine transparente Bitmap mit StretchBlt() im HALFTONE-Modus
  9. Zeichnen Sie eine transparente Bitmap, die jetzt Text enthält, auf die Leinwand des Formulars
  10. Zerstöre beide Bitmaps

Next ist Code zum Beispiel von oben der Seite.

Zuerst erstelle ich eine neue Funktion für die Textausgabe, um den Code im OnPaint-Handler zu bereinigen:

%Vor%

Und als nächstes ist der Code für das OnPaint-Event:

%Vor%     
ThinkJet 19.12.2009 16:27
quelle
1

OK, basierend auf Mghies Vorschlag, die Abstände zwischen den Zeichen hier zu ändern, ist das, was mir einfällt. Ich habe nicht das Array mit Zeichenabstand verwendet, sondern SetTextCharacterExtra und SetTextJustification .

Die Funktion SetTExtCharacterExtra hat folgende Notiz:

  

Diese Funktion wird hauptsächlich für   Kompatibilität mit bestehenden   Anwendungen. Neue Anwendungen sollten   im Allgemeinen vermeiden, diese Funktion aufzurufen,   weil es nicht kompatibel ist mit   komplexe Skripte (Skripte, die erfordern   Textgestaltung; Arabische Schrift ist ein   Beispiel dafür).

     

Der empfohlene Ansatz ist das   anstatt diese Funktion aufzurufen und   dann sollten TextOut, Anwendungen aufrufen   ExtTextOut und verwenden Sie den Parameter lpDx   um Breiten zu liefern.

Ich kann meinen Code ändern, um das zu verwenden, aber im Moment funktioniert dieser Ansatz ziemlich gut. Unten ist meine modifizierte Funktion.

%Vor%     
Mark Elder 22.12.2009 00:44
quelle
1

Es gibt einen Testcode, um verschiedene Lösungen zu vergleichen.
Der Code gibt die tatsächliche Breite der langen skalierten Zeile an die Datei font_cmp.csv aus.

Link zum Bild der Vergleiche

Beispielcode:

%Vor%     
ThinkJet 22.12.2009 07:17
quelle
0

Die Lösung, die von mghie eingeführt wurde, funktioniert gut mit Grafiken, schlägt aber beim Skalieren von Zeichensätzen fehl.
Es gibt eine andere Methode der Skalierung mit entgegengesetzten Eigenschaften: SetWorldTransform . Diese Methode funktioniert gut beim Skalieren von TrueType-Schriftarten, scheitert jedoch beim Zeichnen von Grafiken mit GDI.

Daher ist mein Vorschlag, den DC-Modus mit der Methode mghie zum Zeichnen von Linien zu steuern und SetWorldTransform beim Malen von Text zu verwenden. Ergebnisse nicht so klar, aber sieht noch besser aus ...

Hier ist Code für den OnPaint-Event-Handler, zum Beispiel aus Fragetext, der beide Methoden verwendet:

%Vor%     
ThinkJet 17.12.2009 19:04
quelle

Tags und Links