![]() |
SynPDF und TMetaFile
Hallo Delphianer,
ich bastele gerade an einer Druckvorschau-Komponente. Zum Erzeugen der einzelnen Druckseiten nutze ich TMetafile, Druckvorschau und Ausdruck funktioniert so wie es sein soll. Nun möchte ich als Sahnehäubchen noch eine direkte PDF-Ausgabe dabei haben - also flugs SynPDf eingebunden. Mit folgendem Code gebe ich den Inhalt des Metafiles seitenweise als PDF-Dokument aus:
Delphi-Quellcode:
Mein Problem ist die Ausgabe von Bildern (JPG, PNG ...).
procedure TSDPrintPreview.Document2PDF(FN: string;);
var lPdf : TPdfDocumentGDI; i : integer; TempMetaFile : TMetafile; Stream : TFileStream; begin lPdf := TPdfDocumentGDI.Create; lPdf.ScreenLogPixels := CurrentPrinterRes.X; lPdf.DefaultPaperSize := psA4; //das reicht für meine Zwecke erst mal lPdf.DefaultPageLandscape := FPHeightMM < FPWidthMM; lPdf.ForceNoBitmapReuse := false; lPdf.EmbeddedTTF := true; lPdf.GeneratePDF15File := true; lPdf.ForceJPEGCompression := 90; //Wert vlt. noch skalierbar machen try Stream := TFileStream.Create(FN, fmCreate); try lPdf.SaveToStreamDirectBegin(Stream); //FMFList ist eine TObjectList<TMetaFile>; for I := 0 to FMFList.Count-1 do begin lPdf.AddPage; TempMetaFile := FMFList.Items[I]; TempMetafile.SetSize(FPWidthPrintPxUnscaled,FPHeightPrintPxUnscaled); lPdf.VCLCanvas.StretchDraw(Rect(0,0,TempMetaFile.Width,TempMetaFile.Height),TempMetaFile); lPdf.SaveToStreamDirectPageFlush; // direct writing end; lPdf.SaveToStreamDirectEnd; finally Stream.Free; end; finally lPdf.Free; end; end; Alle im Testdokument enthaltenen Bilder werden korrekt in der Druckvorschau angezeigt oder ausgedruckt. Im erzeugten PDF jedoch fehlen Grafiken vollständig die
Auch Text oder grafische Elemente (Linien, Rechtecke) werden komplett über die gesamte Seitenbreite und -höhe ausgegeben. Es ist also nicht so, dass das Druckbild irgendwie abgeschnitten wird, sondern es fehlen ausschließlich Bilder unter den oben genannten Bedingungen. Hab ich bei der Initialisierung des lPdf-Objektes irgendwas übersehen? Vielen Dank schon mal für eure Mithilfe |
AW: SynPDF und TMetaFile
Das Erstellen von Metafiles wird in bestimmten Zusammenhängen beeinflusst davon,
Ich schreibe mir meine Metafile in einen TMemoryStream, lade mir den PENHMETAHEADER (oder wie das Ding heißt), änder da bestimmte Felder und lade die Metafile dann erneut. |
AW: SynPDF und TMetaFile
Mit dem Bildschirm hat es anscheinend tatsächlich etwas zu tun.
Auf einem 4k-Monitor hab ich den Effekt wie beschrieben, auf einem FullHD-Monitor sind plötzlich gar keine Bilder mehr zu sehen, nur noch Text. Aber der über die volle Blattgröße, da ist nichts abgeschnitten oder fehlt. Ich initialisiere den MetafileCanvas mit
Delphi-Quellcode:
MetaFileCanvas.Font.PixelsPerInch := GetDeviceCaps(printers.Printer.Handle,LOGPIXELSY);
Damit werden die Fonts auf jeden Fall in der richtigen Größe dargestellt. Gibt es so etwas auch für die "allgemeine" Auflösung des MetafileCanvas? Ich finde "PixelsPerInch" in der VCL.Graphics nur im Zusammenhang mit Fonts. Der Canvas des MetaFiles hat auf jeden Fall die richtige Größe in Px, ausgehend von der Druckerauflösung, andernfalls wird der Canavas beim Druck verkleinert poder vergrößert dargestellt. Was mich halt wundert, ist, dass sowohl die Druckvorschau (Darstellung über TMyImage.Picture.Assign(MyMetaFile) ) als auch die Druckausgabe völlig korrekt sind. Nur die PDF-Ausgabe über SynPDF funktioniert nicht richtig. |
AW: SynPDF und TMetaFile
Ich habe auch erstmals auf einem 4K Monitor mit einem Programm bei der Erzeugung von PDF-Dateien (TRichView Komponente mit Gnostice-PDF) festgestellt, dass die Font-Größen und Dimensionierung der Seite nicht mehr stimmten. Ausweg war, das Programm mit GDI-Skalierung zu compilieren dann stimmte es in allen Fällen wieder (habe mir als Workaround eine zweite, nicht sichtbare Fassung des Programms erstellt, welches die PDF-Erzeugung im Falle der Nutzung auf HighDPI Monitoren durchführt). Nicht schön, aber funktioniert erst mal soweit.
Keine Ahnung ob das in Deinem Falle auch weiterhelfen könnte... |
AW: SynPDF und TMetaFile
Ich würde vermuten, dass sich SynPDF da irritieren lässt bzw. die Bugs einfach nicht kennt und daher keinen Workaround einsetzt. Wenn du WMF benutzt, schießt du möglicherweise schnell über die 16 Bit hinaus, die dieses Format maximal für Koordinaten vorsieht. EMF kann 32-Bit-Koordinaten (Enhanced := True). Die Koordinaten hängen von rclFrame ab und rclFrame hängt von der Bildschirmauflösung ab (Verhältnis von szlDevice zu szlMillimeters), genau weiß ich das aber nicht mehr. Das könnte erklären, warum die von dir genannten Grenzen sehr nahe an WQHD sind.
(Fun Fact: Was TMetafileCanvas im not-Enhanced-Modus produziert, ist eine EMF mit eingebetteter WMF (oder war es andersherum?). EMF-fähige Software ignoriert teilweise die WMF.) Für TMetafileCanvas habe ich mir damals dieses Ding hier geschrieben. Da sind auch noch Relikte aus Windows 7 drin und auskommentiert, da diese Software nur noch unter Windows 10 laufen muss. (Parameter Handle ist irgendein DC, man kann sich z.B. eine leere Graphic erstellen, in meinem Fall ein TPNGImage mit Größe 1x1.)
Delphi-Quellcode:
Die hier "behandelten" Metafiles wurden von einer angepassten Version meines RedeemerSVG in der o.g. Klasse TSVGMetafile = class (TMetafile) erzeugt und per List & Label ausgegeben. List & Label basiert selbst komplett auf EMF, sprich alle angezeigten Dokumente liegen im Temp-Ordner mit einer EMF pro Seite.
procedure TSVGMetafile.FixRDPBug(const Handle: HDC);
var Temp: TMemoryStream; Header: PENHMETAHEADER; begin // Workaround für einen Fehler in RDP, durch den szlMillimeters und rclFrame falsch sind, wenn der Hauptbildschirm des verbindenden Rechners nicht das Seitenverhältnis 4:3 hat // vgl. https://stackoverflow.com/a/1533053 // Kann leider nicht beim geladenen Bild geändert werden, da man an szlDevice (HORZRES und VERTRES bei GetDeviceCaps) nicht im Speicher des vorhandenen Bildes ran kommt // Bug scheint in Windows 10 nicht mehr zu existieren. // Nachtrag 15.6.21: Dafür existiert ein ähnlicher, auf dieselbe Weise behebbarer Bug: // - Beim ursprünglichen RDP-Bug waren HORZSIZE und VERTSIZE zwar gleich szlMillimeters, aber falsch in Relation und im Verhältnis zu HORZRES und VERTRES. // - Beim neuen Bug sind HORZSIZE und VERTSIZE gleich szlMillimeters und korrekt, aber HORZRES und VERTRES sind ungleich szlDevice. // Besteht KEINE Remote-Desktop-Verbindung? Dann Abbruch. // Keine Ahnung, ob das Anfang August 2019 funktioniert hat, aber ich habe jetzt Ende Oktober 2019 keinen Self.Handle mehr, die Methode gibt daher 0 zurück, was anders als 320 bzw. 240 ist. // Daher wird der Handle von TMetafileCanvas übergeben. //if GetDeviceCaps(Handle, HORZSIZE) <> 320 then Exit; //if GetDeviceCaps(Handle, VERTSIZE) <> 240 then Exit; // Theoretisch können diese Werte auch ohne RDP auftreten, wenn der Nutzer einen 4:3-Bildschirm besitzt. // FixRDPBug macht korrekte Bilder nicht kaputt, daher geht es hier ausschließlich um die Performance, die bei einem Durchlauf des folgenden Codes verloren geht. // 15.6.21: Obige Zeilen auskommentiert da Kriterien für Bildschirmskalierungs-Bug unzureichend. Kriterien können erst nach dem Laden bestimmt werden. Temp := TMemoryStream.Create(); try Self.SaveToStream(Temp); if Temp.Size > 0 then begin Temp.Position := 0; Header := Temp.Memory; if (Header^.szlDevice.cx = GetDeviceCaps(Handle, HORZRES)) and (Header^.szlDevice.cy = GetDeviceCaps(Handle, VERTRES)) then Exit; Header^.rclFrame.Right := (Header^.rclBounds.Right + 1) * 100; Header^.rclFrame.Bottom := (Header^.rclBounds.Bottom + 1) * 100; Header^.szlDevice.cx := 320; // gleiche Werte für beide szl-Felder machen es einfacher, Rundungsfehler hierüber zu vermeiden (man kann einfach mit 100 multiplizieren) Header^.szlDevice.cy := 240; Header^.szlMillimeters.cx := 320; Header^.szlMillimeters.cy := 240; inherited LoadFromStream(Temp); end; finally Temp.Free(); end; end; Zurück zu meinem Problem: Der Fehler äußerte sich vorrangig darin, dass das Bild gestaucht wurde (ein quadratisches Bild wurde hochkant), wenn man mit einem Breitbildmonitor über RDP verbunden war. Die Koordinaten zu korrigieren brachte nichts, denn dann waren horizontale Linien dicker als vertikale. Laut PENHMETAHEADER verwendet Windows RDP hardcoded eine Auflösung von 320x240. Für alle, die es interessiert, ein paar wirre Informationen aus meinem OneNote, größtenteils über Windows 7: Zitat:
|
AW: SynPDF und TMetaFile
Vielen Dank für Eure Hinweise. Leider hatte mich das irgendwie doch nicht weitergebracht.
Hab mir daraufhin SynPDF noch einmal etwas genauer angeschaut und bin auf eine zweite Methode gestoßen, MetaFiles zu rendern:
Delphi-Quellcode:
lPdf.Canvas.RenderMetaFile(TempMetafile, 1, 0, 0, 0,lPdf.UseMetaFileTextPositioning, lPdf.KerningHScaleBottom, lPdf.KerningHScaleBottom,tcNeverClip);
Und genau das hat es gebracht. Ursprünglich hatte ich das Metafile über
Delphi-Quellcode:
lPdf.VCLCanvas.StretchDraw(Rect(0,0,TempMetaFile.Width,TempMetaFile.Height),TempMetaFile);
ausgegeben. Der VCLCanvas soll, laut eigener Aussage der Entwickler von Synopse besser geeignet sein, wenn man direkt darauf zeichnen will, weil er die gleichen Methoden wie der bekannte Canvas aus Delphi hat. Zum Rendern von Metafiles ist er aber offensichtlich nicht so gut geeignet. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz