AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Eigene Druckvorschau mit TMetafile und TImage

Ein Thema von runningsoft · begonnen am 4. Jan 2021 · letzter Beitrag vom 5. Jan 2021
Antwort Antwort
Benutzerbild von runningsoft
runningsoft

Registriert seit: 8. Okt 2004
Ort: Bernau
108 Beiträge
 
Delphi 10.4 Sydney
 
#1

Eigene Druckvorschau mit TMetafile und TImage

  Alt 4. Jan 2021, 17:18
Hallo zusammen,

ich bin gerade dabei eine eigene Druckvorschau zu erstellen (ich weiß, dafür gibts fertige Komponenten, die möchte ich aber nach Möglichkeit nicht nutzen).

Ich gehe dabei für jede einzelne Druckseite folgendermaßen vor:

-TMetafile erstellen
-auf TMetafileCanvas schreiben / Zeichnen, was auch immer

wenn Seitenende erreicht:
-unsichtbares TImage für die spätere Anzeige auf einem Panel erzeugen
-Inhalt des MetaFiles per StretchDraw in Image kopieren
-TMetafile wieder freigeben
-für neue Seite neues TMetaFile erstellen usw.

Das funktioniert soweit wunderbar, ich hab testweise 200 Druckseiten erzeugt (reiner Text), durch die ich dann ohne sichtbare Zeitverzögerung blättern konnte, indem ich einfach die betreffenden TImages sichtbar gemacht habe.

Was nicht funktioniert ist ein nachträglices Zoomen. Die ursprünglichen Metafiles, die man beliebig Zoomen könnte, hab ich nicht mehr. Wenn man die Images zoomt, wirds natürlich pixelig.

Deshalb meine Frage: Kann man die MetaFiles jeder einzelnen Seite irgendwie im Arbeitsspeicher halten, so dass ich jede einzelne TImage nach dem zoomen mit dem neuen Zoomfaktor neu schreiben kann?
Meine Idee war, beliebig viele Metafiles zur Laufzeit zu erzeugen (für jede Druckseite eines). Leider gibt es jedoch keine Eigenschaft TMetafile.Name, so dass ich daran bisher gescheitert bin.

Was könnte ich noch machen?
Könnte man eine Art "MetaFileListe" (TList ???) erstellen, der ich nach dem Erzeugen einer Seite die Daten des MetaFiles hinzufügen kann, so dass man beim Zoomen auf die Einträge in der MetafileListe zugreifen kann?
  Mit Zitat antworten Zitat
BerndS

Registriert seit: 8. Mär 2006
Ort: Jüterbog
491 Beiträge
 
Delphi 12 Athens
 
#2

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 4. Jan 2021, 17:48
Hallo,
solltes du Zugriff auf die Quickreport Quellen haben, kannst du dir da sicher was abschauen.
Da gibt es z.B. in der Unit QRPrntr die TQRPageList und dazu passend Units zum Anzeigen und Drucken.

Gruß Bernd
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.624 Beiträge
 
Delphi 12 Athens
 
#3

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 4. Jan 2021, 17:49
Oder vielleicht auch mal hier schauen: http://www.delphiarea.com/products/d...nents/preview/
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
TurboMagic

Registriert seit: 28. Feb 2016
Ort: Nordost Baden-Württemberg
2.942 Beiträge
 
Delphi 12 Athens
 
#4

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 4. Jan 2021, 19:48
Mit Sydney hast du doch Generics. Warum nicht eine
TObjectList<TMetaFile>?

Die kann dann auch die Referenz freigeben, da OwnsObject Semantik
eingebaut ist.
  Mit Zitat antworten Zitat
Benutzerbild von runningsoft
runningsoft

Registriert seit: 8. Okt 2004
Ort: Bernau
108 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 4. Jan 2021, 23:27
Der Ansatz von TurboMagic ist glaub ich das, was ich möchte, allein: es funktioniert leider noch nicht

Ich habe bisher folgendes:
Delphi-Quellcode:
var
  fmVorschau: TfmVorschau;
  AnzPages, AktPage : integer;
  PrevMeta : TMetaFile;
  PrevMetaCanvas : TMetaFileCanvas;
  MFList : TObjectList<TMetaFile>;

procedure TfmVorschau.FormCreate(Sender: TObject);
var I : integer;
begin
AnzPages := 1;
AktPage := 1;
MFList := TObjectList<TMetafile>.Create;
end;

procedure TfmVorschau.CreateMetaFile;
begin
PrevMeta := TMetafile.Create;
PrevMeta.Enhanced := true;
PrevMeta.SetSize(PWidth,PHeight); //Größe des Metafiles, je nach Papierformat
PrevMetaCanvas := TMetafileCanvas.Create(PrevMeta,0); //Canvas erzeugen
PrintUtils.AktCanvas := PrevMetaCanvas; //aktuellen Canvas für Unit PrintUtils festlegen, diese enthält die Basisdruckroutinen
SetMapMode(AktCanvas.Handle,MM_TEXT);
SetTextAlign(AktCanvas.Handle,TA_BaseLine);
end;

...

procedure TfmVorschau.NewPage;
begin
//Freigabe des MetafileCanvas der VORHERIGEN Druckseite
PrevMetaCanvas.Free;
//PrevMeta.SaveToFile('D:\Test_Firebird_Server\' + IntToStr(AnzPages) + '.emf'); //diese Variante funktioniert

//Metafile der VORHERIGEN Druckseite in Objektliste ablegen und freigeben
MFList.Add(PrevMeta);
PrevMeta.Free;

//neue Seite erzeugen
Inc(AnzPages);
CreateMetafile; //neues Metafile für nächste Seite erzeugen
end;

//Abschlussprozedur nach dem Erzeugen der letzten Druckseite
procedure TfmVorschau.FinishPreview;
begin
PrevMetaCanvas.Free;
//PrevMeta.SaveToFile('D:\Test_Firebird_Server\' + IntToStr(AnzPages) + '.emf'); //siehe oben
MFList.Add(PrevMeta);
PrevMeta.Free;
end;

...

//die Anzeigeprozedur produziert nur leere Seiten
//die Liste weist genau so viele Einträge auf, wie Seiten vorher gedruckt und in der Liste abgelegt wurden
procedure TfmVorschau.ShowAktPage;
var CurrMetaFile : TMetafile;
begin
CurrMetaFile := TMetafile.Create;
//CurrMetaFile.LoadFromFile('D:\Test_Firebird_Server\' + IntToStr(AktPage) + '.emf'); //Anzeige funktioniert auf diese Art und Weise

//Eintrag der gewünschten Seite auf Metafile-Liste holen --> ist das richtig so ???
CurrMetafile := MFList.Items[AktPage-1];
    
//Seitengröße entsprechend der gewählten Zoomstufe einstellen
CurrMetaFile.SetSize(PageP1.Width,PageP1.Height);

//mit dem Vorschauimage verbinden
PrevIMG.Picture.Assign(CurrMetaFile);

//Freigabe
CurrMetafile.Free;
Label1.Caption := 'Seite ' + IntToStr(AktPage);
end;
Wie schon im Quelltext angemerkt, funktioniert das Ganze, wenn ich jede einzelne Druckseite als Datei auf Festplatte abspeichere und für die Anzeige wieder einlese. Das will ich natürlich nicht.
Hab ich einen Fehler in der Prozedur des Ablegens der Metafiles in der Objektliste oder in der Prozedur für das Auslesen aus dieser?

Geändert von runningsoft ( 4. Jan 2021 um 23:35 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.624 Beiträge
 
Delphi 12 Athens
 
#6

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 5. Jan 2021, 07:26
In CreateMetaFile vermisse ich das Hinzufügen der angelegten Instanz zur Liste.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#7

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 5. Jan 2021, 07:35
Moin...
Delphi-Quellcode:

procedure TfmVorschau.CreateMetaFile;
begin
PrevMeta := TMetafile.Create;
PrevMeta.Enhanced := true;
PrevMeta.SetSize(PWidth,PHeight); //Größe des Metafiles, je nach Papierformat
PrevMetaCanvas := TMetafileCanvas.Create(PrevMeta,0); //Canvas erzeugen
PrintUtils.AktCanvas := PrevMetaCanvas; //aktuellen Canvas für Unit PrintUtils festlegen, diese enthält die Basisdruckroutinen
SetMapMode(AktCanvas.Handle,MM_TEXT);
SetTextAlign(AktCanvas.Handle,TA_BaseLine);
end;

...

procedure TfmVorschau.NewPage;
begin
//Freigabe des MetafileCanvas der VORHERIGEN Druckseite
PrevMetaCanvas.Free;
//PrevMeta.SaveToFile('D:\Test_Firebird_Server\' + IntToStr(AnzPages) + '.emf'); //diese Variante funktioniert

//Metafile der VORHERIGEN Druckseite in Objektliste ablegen und freigeben
MFList.Add(PrevMeta);
PrevMeta.Free;

//neue Seite erzeugen
Inc(AnzPages);
CreateMetafile; //neues Metafile für nächste Seite erzeugen
end;

//Abschlussprozedur nach dem Erzeugen der letzten Druckseite
procedure TfmVorschau.FinishPreview;
begin
PrevMetaCanvas.Free;
//PrevMeta.SaveToFile('D:\Test_Firebird_Server\' + IntToStr(AnzPages) + '.emf'); //siehe oben
MFList.Add(PrevMeta);
PrevMeta.Free;
end;
Unklarheiten:
Ich verstehe das so...Jede Seite besteht aus einem PrevMeta und immer einem PrevMetaCanvas?
1. Der MetaCanvas ist imho ein Teil des PrevMeta. Warum ist PrevMetaCanvas dann keine Property von PrevMeta? Das macht die Freigabe einfacher...
2. Warum nicht alle Druckseiten erzeugen und in die Liste legen? Und dann die Liste durchiterieren und drucken bzw. anzeigen?
3.
Delphi-Quellcode:
procedure TfmVorschau.FinishPreview;
begin
PrevMetaCanvas.Free; // Freigabe des Canvas
MFList.Add(PrevMeta); // MetaData in die Liste packen
PrevMeta.Free; // MetaData freigeben
end;
Warum erst in die Liste aufnehmen und gleich wieder freigeben? Erst am Ende die Liste komplett freigeben!
4. Bitte keine globalen Variablen.

Vorschlag (nur das Wichtigste):
Delphi-Quellcode:
interface
   // Muster
   TMetaFileOwn = class(TMetaFile) // oder so
   strict private
     FMetaFileCanvas: TMetaFileCanvas;
   public
     constructor Create;
     destructor Destroy; override;
     property MetaFileCanvas: TMetaFileCanvas read FMetaFileCanvas write FMetaFileCanvas
   end;
   // Muster

   TfmVorschau = class(TForm)
   strict private
     FAnzahlPages: Integer;
     FAktPage: Integer;
     FCurrentMetaData: TMetaData; // die aktuell gewählten Daten aus der Liste (Previous / Prior über Buttons etc.)
     FMFList: TObjectList<TMetaFileOwn>;
   public
   end;

var
  fmVorschau: TfmVorschau;

implementation

constructor TMetaFileOwn.Create;
begin
  inherited;
  FMetaFileCanvas := TMetaFileCanvas.Create(Self, 0); //Canvas erzeugen, Self = TMetaFile;
end;

destructor TMetaFileOwn.Destroy; override;
begin
  FMetaFileCanvas.Free;
end;

procedure TfmVorschau.FormCreate(Sender: TObject);
begin
  AktPage := 1;
  FMFList := TObjectList<TMetaFileOwn>.Create;
end;

procedure TfmVorschau.FormDestroy(Sender: TObject);
begin
  FinishPreview; // Liste leeren nicht nötig
  FMFList.Free; // Liste leeren macht Free sowieso...nur als Erklärung
end;

procedure TfmVorschau.CreateMetaFile;
begin
  FCurrentMetaData := TMetaFileOwn.Create;
  FCurrentMetaData.Enhanced := true;
  FCurrentMetaDataSetSize(PWidth, PHeight); //Größe des Metafiles, je nach Papierformat
  PrintUtils.AktCanvas := FCurrentMetaData.MetaFileCanvas; //aktuellen Canvas für Unit PrintUtils festlegen, diese enthält die Basisdruckroutinen
  SetMapMode(AktCanvas.Handle, MM_TEXT);
  SetTextAlign(AktCanvas.Handle, TA_BaseLine);
  FMFList.Add(FCurrentMetaData); // eine nach der Anderen in die Liste
end;

procedure TfmVorschau.NewPage;
begin
  CreateMetafile; //neues Metafile für nächste Seite erzeugen
  FAnzahlPages := FMFList.Count;
end;

procedure TfmVorschau.ShowPreview; // irgendwo müssen die Seiten auch angezeigt werden
begin
  Show(FMFList); // der Anzeigeprocedure die Liste übergeben
end;

procedure TfmVorschau.FinishPreview;
begin
  FMFList.Clear; // alle Objekte freigegeben
end;

Geändert von haentschman ( 5. Jan 2021 um 08:20 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von runningsoft
runningsoft

Registriert seit: 8. Okt 2004
Ort: Bernau
108 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Eigene Druckvorschau mit TMetafile und TImage

  Alt 5. Jan 2021, 11:15
Vielen Dank für die Hinweise und Denkanstöße. es hat mich noch mal 2 Stunden gekostet, aber jetzt funktioniert es.

@DeddyH: Ich war der Meinung, dass man erst das "gefüllte" Metafile, also nachdem die Seite geschrieben wurde, der Objektliste anhängen kann, deshalb hatte ich das nicht sofort in der CreateMetaFile - Prozedur gemacht.

Der entscheidende Hinweis war aber dann der von haentschman
Zitat:
Warum erst in die Liste aufnehmen und gleich wieder freigeben? Erst am Ende die Liste komplett freigeben!
Genau da lag mein Fehler. Man kann letztlich beliebig viele Objekte vom gleichen Typ (hier TMetafile) erzeugen und der Liste hinzufügen. Freigegeben werden Sie erst wieder mit der Freigabe der kompletten Liste. Dies passiert, wenn ich das richtig verstanden habe, automatisch, wenn die generische Objektliste Owner der Objekte ist

MFList.OwnsObjects := true; oder gleich
MFList := TObjectList<TMetafile>.Create(true);
Falls mal jemand ein ähnliches Problem hat, hier noch die entscheidenden Punkte des Quelltextes mit ein paar Kommentaren:
Delphi-Quellcode:
private
    { Private-Deklarationen }
    var AktPage, PWidth, PHeight : integer;
        FScale : real;
        MFList : TObjectList<TMetaFile>; //generische Objektliste zur Speicherung der Metafiles
        PrevListMeta : TMetaFile; //Metafile, das in die Objektliste eingefügt wird
        PrevMetaCanvas : TMetaFileCanvas; //MetaCanvas, auf dem gezeichnet wird
        ZoomManuell: Boolean;

procedure TfmVorschau.FormCreate(Sender: TObject);
var I : integer;
begin
AktPage := 1;
MFList := TObjectList<TMetafile>.Create(true);
end;

procedure TfmVorschau.FormShow(Sender: TObject);
begin
//Ausgehend von der Pixeldichte des Monitors die Größe des Panels bei 100% Darstellung berechnen
// z.B. 96ppi / 25.4 (=ppm) * 210 mm
PWidth := trunc(Screen.PixelsPerInch / 25.4 * AktFormatArr[1]);
PHeight := trunc(Screen.PixelsPerInch / 25.4 * AktFormatArr[2]);
InitPreview(AktFormatArr[1],AktFormatArr[2]);
CreateMetafile;
end;

procedure TfmVorschau.CreateMetaFile;
begin
PrevListMeta := TMetafile.Create;
PrevListMeta.Enhanced := true;
PrevListMeta.SetSize(PWidth,PHeight); //Größe des Metafiles, je nach Papierformat
MFList.Add(PrevListMeta); //uch wenn das Metafile noch leer ist, kann es schon der Liste hinzugefügt werdena

//Canvas erzeugen, auf dem gezeichnet wird
PrevMetaCanvas := TMetafileCanvas.Create(PrevListMeta,0);

//als aktuellen Canvas an unit PrintUtils geben
//für realen Druck wird in anderer Prozedur Printer.Canvas als AktCanvas festgelegt
PrintUtils.AktCanvas := PrevMetaCanvas;
SetMapMode(AktCanvas.Handle,MM_TEXT);
SetTextAlign(AktCanvas.Handle,TA_BaseLine);
end;

procedure TfmVorschau.NewPage;
begin
//MetaCanvas der vorherigen Seite freigeben
//erst nach Freigabe des MetaCanvas werden die darauf enthaltenen Daten an das
//Metafile übergeben
PrevMetaCanvas.Free;

//PrevMeta.Free; //--> das war der Fehler, weshalb die Objekte in der Objektliste leer waren
                  // die erzeugten Metafiles dürfen nicht sofort wieder freigegeben werden
                  // sondern erst mit der Freigabe der Objektliste beim dem Schließen der Form

//neues Metafile für nächste Seite erzeugen
//dort wird dann auch wieder ein neuer MetaCanvas erzeugt, auf dem gezeichnet wird
CreateMetafile;
end;

procedure TfmVorschau.LastPage;
var i: integer;
    Temp: TComponent;
begin
//siehe Kommentar NewPage
PrevMetaCanvas.Free;
end;

...

procedure TfmVorschau.ShowAktPage;
var PrevIMGMeta : TMetaFile;
begin
//Eintrag aus Objektliste in Metafile übernehmen
PrevIMGMeta := MFList.Items[AktPage-1];
//Seitengröße auf aktuelle Zoomstufe anpassen
PrevIMGMeta.SetSize(PageP1.Width,PageP1.Height);
//TImage.Picture mit Metafile verbinden
PrevIMG.Picture.Assign(PrevIMGMeta);

Label1.Caption := 'Seite ' + IntToStr(AktPage);
end;
Vielen Dank für eure Hilfe

Geändert von runningsoft ( 5. Jan 2021 um 11:21 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz