|
Antwort |
Registriert seit: 29. Mär 2008 Ort: D-49626 381 Beiträge Delphi 11 Alexandria |
#11
Wie hab ich's nun gemacht:
Global verwende ich 3x TRichEdit - RE_SRC hält den Source (aus zu ladender Datei), RE_TMP ist für temporäre Suche und RE_PAGE bekommt den zu druckenden RTF-Inhalt. Aus RE_SRC hole ich mir den Plain-Text via Stream. Darin suche ich erst einmal das Key-Wort '\page', um die Pos. des 1. Seitenumbruch im Source zu suchen. Kleiner Nebeneffekt: Der TRichEdit-Parser hat ne ganze Menge "Müll" aus dem Text entfernt - aber gewünschte '\page' oder '\pagebb' sind drin geblieben ! Nun loope ich durch RE_SRC.Lines[], nehme den Text daraus und suche wieder im Source von RE_SRC nach dem Anfang des Textes. Ist die Text-Pos. > als die Page-Pos., gehört die aktuelle Line[] auf die nächste Seite. Dann wieder neu nach '\page' suchen und weiter mit der nächsten Zeile .... bis alle Zeilen durchlaufen. Funzt sehr gut, ABER... ... nur solange der RTF-Text keine Umlaute (z.B. ein "ä" ist dann \'e4) oder "RTF-Sonderzeichen" enthält (z.B. '...' ist ein "ellipsis" - \'85). Im formatierten Text sieht man das logo nicht, aber ein Textvergleich mit dem Source via Pos/PosEx ergibt -1 weil keine Übereinstimmung. Also ein bischen Tricky: Ich nehme also den Text und erzeuge damit im RE_TMP eine Zeile. Diese lese ich dann aus dem Stream wieder aus und habe somit wieder die ursprüngliche Formatierung, womit ich dann vergleichen kann. 2. Problem: Enthält der Text nun auch noch Formatierungen wie Farbe, andere Schriftgröße, Bold, etc., dann stehen noch mehr Formatierungszeichen im Source, die leider nicht mit vorgenanntem Trick wieder auftauchen. Ich habe dazu bisher noch keine Lösung gefunden - vielleicht hat hier ja jemand eine Idee !? Hier die beiden proceduren:
Delphi-Quellcode:
Zum 'merken' welche Seiten welche Zeilen enthalten, benutze ich ein TList, wo ich an jedes neue Item (die Seite) einen Record mit den Zeilen From-To einhänge.
// -----------------------------------------------------------------------------
procedure TForm1.RTF_Parser(Datei : string); var SS : TStringStream; s, s2 : string; i, PosStr, Ppage : longint; LineFrom, LineTo : integer; begin Memo.Clear; ClearPagelList; RE_SRC.Clear; RE_TMP.Clear; RE_PAGE.Clear; RE_SRC.Lines.LoadFromFile(Datei); if (RE_SRC.Lines.Count < 1) then begin Memo.Lines.Add('... nothing to print !'); exit; end; SS := TStringStream.Create(''); RE_SRC.Lines.SaveToStream(SS); SS.Position := 0; Memo.Lines.Add('Num of Lines to print: ' + inttostr(RE_SRC.Lines.Count)); PosStr := 1; Ppage := PosEx('\page', SS.DataString, PosStr); // read also '\pagebb' LineFrom := 0; LineTo := 0; for I := 0 to RE_SRC.Lines.Count - 1 do begin s := RE_SRC.Lines[i]; if (S = '') then continue; s2 := Get_RTF_plain(s); PosStr := Pos(s2, (SS.DataString)); if (Ppage > 0) AND (PosStr > Ppage) then begin LineTo := i - 1; AddPage2List(LineFrom, LineTo); // Add Page to list: LineFrom := LineTo + 1; Ppage := PosEx('\page', SS.DataString, PosStr); // read also '\pagebb' end; end; AddPage2List(LineFrom, RE_SRC.Lines.Count - 1); SS.Free; end; // ----------------------------------------------------------------------------- function TForm1.Get_RTF_plain(s : string) : string; var SS : TStringStream; p1, p2 : integer; begin SS := TStringStream.Create(''); RE_tmp.Clear; RE_tmp.SelText := s; RE_tmp.Lines.SaveToStream(SS); p1 := PosEx('\pard', (SS.DataString), 1); p1 := PosEx(' ', (SS.DataString), p1); p2 := PosEx('\par', (SS.DataString), p1 + 1); ss.Position := p1; result := ss.ReadString(p2 - p1 - 1); SS.Free; end;
Delphi-Quellcode:
Zum Drucken des jeweilige Seiteninhalts kopiere ich zunächst das RE_SRC via Stream in RE_PAGE, damit die Formatierungen bleiben.
// -----------------------------------------------------------------------------
// http://delphi.xcjc.net/viewthread.php?tid=43702 procedure TForm1.AddPage2List(LineFrom, LineTo : integer); var Item : PRTFPageData; begin // PageNum is Itemindex + 1 New(Item); Item.LFrom := LineFrom; Item.LTo := LineTo; FPageList.Add(Item); cbb_SelPage.Items.Add('- Seite ' + inttostr(FPageList.Count)); Memo.Lines.Add('!!! Page added: ' + inttostr(FPageList.Count) + ' (Lines ' + inttostr(LineFrom) + ' - ' + inttostr(LineTo) + ')'); end; Um nur den gewünschten Inhalt zu bekommen, lösche ich alle nicht gewünsvhten Zeile einfach aus RE_Page heraus. ACHTUNG: Ich lese die Zeilen rückwärts mit for ... downto, weil sonst (von vorne gelesen) die Zeilennummern nach Löschen nicht mehr passen.
Delphi-Quellcode:
Das läuft hier schon sehr sauber - muss aber logo betreff 'Eleganz' noch ein bischen überarbeitet werden.
// -----------------------------------------------------------------------------
// RE_SRC is the RTF-Source by loading !!! procedure TForm1.PrintRTF(PageFrom, PageTo : integer); var i, LF, LT : integer; begin if (PageFrom < 0) then begin // => Print all Memo.Lines.Add('... now Printing all pages'); SelectLines(0, RE_SRC.Lines.Count - 1); // call your favourite RTF-printing-service with RE_SRC !!! end else for i := PageFrom to PageTo do begin Memo.Lines.Add('... now Printing page: ' + inttostr(i)); LF := PRTFPageData(FPageList[i - 1]).LFrom; LT := PRTFPageData(FPageList[i - 1]).LTo; SelectLines(LF, LT); // call your favourite RTF-printing-service with RE_PAGE.... end; end; // ----------------------------------------------------------------------------- // RE_SRC is the RTF-Source by loading !!! // New RTF-page remains in RE_PAGE !!! // http://delphi.xcjc.net/viewthread.php?tid=43702 procedure TForm1.SelectLines(LineFrom, LineTo : integer); var i : integer; MS : TMemoryStream; begin if (LineTo < LineFrom) then begin Memo.Lines.Add(' ERROR: Invalid Lines to print'); exit; end; Memo.Lines.Add('--- SelectLinesFromSource:' + ' - Max: ' + inttostr(RE_SRC.Lines.count) + ' - from '+ inttostr(LineFrom + 1) + ' to ' + inttostr(LineTo + 1)); // 1st: copy RE to RE_PAGE MS := TMemoryStream.Create; try RE_SRC.Lines.SaveToStream(MS); MS.Position := 0; RE_PAGE.Lines.LoadFromStream(MS) ; finally MS.Free; end; // 2nd: Loop through all RE_SRC-lines and delete unwanted lines // !!! walking BACKWARDS through list of lines !!! for i := RE_PAGE.Lines.Count - 1 downto 0do begin if (i < LineFrom) then RE_PAGE.Lines.Delete(i); if (i > LineTo) then RE_PAGE.Lines.Delete(i); end; // call your favourite RTF-printing-service with RE_PAGE.... end; Schön wäre es natürlich, wenn auch formatierter Text zum Seitenumbruch richtig erkannt würde. Ich hab das panze Test-Proggie mal eingepackt. Ich hoffe das ist ausreichend selbsterklärend. Kompiliert mit D2009 unter Win 8.1 x64. Es wird nur Standard verwendet. Dazu noch meine 3 Test-RTFs, mit LibreOffice erzeugt. NACHTRAG: Die procedure Get_RTF_plain ist auch eher etwas "Brute Force", ohne den Source hier nun in Blöcke zu zerlegen und umständlich im Detail zu parsen. Ich habe bisher noch nirgends was einfaches gefunden, wie man konkret in einem RTF den Anfang einer Textzeile erkennt. Angeblich beginnt die immer mit einem " " - Angaben zu Zeichensätzen u. a. aber auch ... Geändert von TERWI (11. Dez 2017 um 12:41 Uhr) |
Zitat |
Registriert seit: 29. Mär 2008 Ort: D-49626 381 Beiträge Delphi 11 Alexandria |
#12
... es geht noch einfacher, besser und universeller.
Ein'\page' oder '\pagebb' steht immer irgendwo mit anderen Steuerzeichen vor dem Text, der auf die nächste Seite soll. Ich ersetze nun einfach diese Steuerreichen mit einem Text (hier: '|NEWPAGE|'), der auch anschließend im RTF lesbar am Anfang der Zeike steht. Anschließend loope ich alle Zeilen im Richedit und prüfe auf eben diesen Text. Sofern vorhanden, wird ein Seitenwechsel vermerkt und der Text einfach wieder aus der Richedit-Zeile gelöscht. Das funktioniert 1A - egal ob der Text plain oder irgendwie Formatiert ist, Umlaute oder Sonderzeichen hat.
Delphi-Quellcode:
Die procedure für die zu druckenden Seiten hab ich nun auch korrigiert/ergänzt:
///-----------------------------------------------------------------------------
procedure TForm1.Search4Pages(Datei : string); var SS : TStringStream; MS : TMemoryStream; RTFplain : ANSIString; // shoulb be NOT a normal (wide) String !!! i, LineFrom : integer; begin SS := TStringStream.Create(''); MS := TMemoryStream.Create; ClearPagelList; Memo.Clear; RE_SRC.Clear; RE_PAGE.Clear; // ... load the file (or stream) SS.LoadFromFile(Datei); SetLength(RTFplain, SS.Size); SS.Read(RTFplain[1], SS.Size); // or SS.ReadBuffer !? RTFplain := StringReplace(RTFplain, '\pagebb', ' |NEWPAGE|', [rfReplaceAll]); RTFplain := StringReplace(RTFplain, '\page', ' |NEWPAGE|', [rfReplaceAll]); MS.Position := 0; MS.WriteBuffer(RTFplain[1],Length(RTFplain)); MS.Position := 0; RE_SRC.Lines.LoadFromStream(MS); SS.Free; // no longer necessary MS.Free; // no longer necessary // ... now loop through (displayed) lines, recognize FormFeed and delete PlaceHolder LineFrom := 0; for i := 0 to RE_SRC.Lines.Count - 1 do begin RE_SRC.Lines.BeginUpdate; if Pos('|NEWPAGE|', RE_SRC.Lines[i]) > 0 then begin RE_SRC.Lines[i] := StringReplace(RE_SRC.Lines[i], '|NEWPAGE|', '', []); // [rfReplaceAll]); AddPage2List(LineFrom, i - 1); // Add Page to list: LineFrom := i; // LineTo + 1; end; RE_SRC.Lines.EndUpdate; end; AddPage2List(LineFrom, RE_SRC.Lines.Count - 1); end;
Delphi-Quellcode:
// -----------------------------------------------------------------------------
// PageFrom: Min = 1, Max = FPageList.Count // PageTo: Min = PageFrom, Max = FPageList.Count procedure TForm1.PrepRTFtoPrint(PageFrom, PageTo : integer; All : boolean); var i, ii, LF, LT : integer; MS : TMemoryStream; begin if (FPageList.Count < 1) then exit; // no page to print if All then // print all pages ... begin PageFrom := 1; PageTo := FPageList.Count; end; if (PageFrom < 0) then exit; // range to low if (PageFrom > FPageList.Count) then exit; // range to high if (PageTo < PageFrom) then exit; // range to low if (PageTo > FPageList.Count) then exit; // range to high // ... print valid pages for i := PageFrom to PageTo do begin LF := PRTFPageData(FPageList[i - 1]).LFrom; LT := PRTFPageData(FPageList[i - 1]).LTo; Memo.Lines.Add('... Printing page ' + inttostr(i) + ' - Line ' + inttostr(LF) + ' to ' + inttostr(LT)); // 1st: copy RE_SRC to RE_PAGE MS := TMemoryStream.Create; try RE_SRC.Lines.SaveToStream(MS); MS.Position := 0; RE_PAGE.Lines.LoadFromStream(MS) ; finally MS.Free; end; // 2nd: Loop through all RE_SRC-lines and delete unwanted lines // !!! walking BACKWARDS through list of lines !!! for ii := RE_PAGE.Lines.Count - 1 downto 0 do begin if (ii < LF) then RE_PAGE.Lines.Delete(ii); if (ii > LT) then RE_PAGE.Lines.Delete(ii); end; // 3rd: call your favourite RTF-printing-service with RE_PAGE.... end; end; |
Zitat |
Registriert seit: 29. Mär 2008 Ort: D-49626 381 Beiträge Delphi 11 Alexandria |
#13
Ein Update:
Das mit den Zeilen löschen war ja schon recht praktikabel, aber wenn mann im (Source-) RE vergisst, das WordWrap abzustellen, geht das richtig in Hose. Neue und bessere Idee: Ich lösche nun im RE.Text betreffende Textstellen. Hier mal ein Auszug aus meinem Test-Prog in Verbindung mit TPrintPreview:
Delphi-Quellcode:
Wie das mit der Compo TPrintPreview sauber mit Multi-File funzt, kommt in einem neuen Fred.
// -----------------------------------------------------------------------------
// GLOBAL: - FFrameRect // - RE_PAGE // - RE_SRC procedure TMainForm.RenderRichEdit(Files : TStringList); procedure RenderPage(); var TextRect: TRect; Offset: Integer; begin Offset := 0; while (Offset >= 0) and (PrintPreview.PaintRichText(FFrameRect, RE_Page, 1, @Offset) <> 0) do begin Application.ProcessMessages; if (Offset >= 0) then PrintPreview.NewPage; end; end; var SS : TStringStream; MS : TMemoryStream; RTFplain : ANSIString; // should be NOT a wide string !!! i, l, p : integer; begin PrintPreview.BeginDoc; PrintPreview.Units := mmHiMetric; // All units are in 1/100th of millimeter CalcFrame; // ... loop the files for I := 1 to Files.Count do begin SS := TStringStream.Create(''); MS := TMemoryStream.Create; RE_SRC.Clear; RE_PAGE.Clear; // ... load file to stream and copy to ANSI-STRING !!! (NOT WIDE) SS.LoadFromFile(Files[i-1]); SetLength(RTFplain, SS.Size); SS.Read(RTFplain[1], SS.Size); // - replace \page(bb) with ' |NEWPAGE|' as readable text for FORMFEED RTFplain := StringReplace(RTFplain, '\pagebb', ' |NEWPAGE|', [rfReplaceAll]); // RTFplain := StringReplace(RTFplain, '\page', ' |NEWPAGE|', [rfReplaceAll]); MS.Position := 0; MS.WriteBuffer(RTFplain[1],Length(RTFplain)); // load back to stream MS.Position := 0; RE_SRC.Lines.LoadFromStream(MS); // load back to RTF-Source SS.Free; // ... now look for the new placeHolder(s) repeat MS.Clear; // ... copy source-RTF to print-RTF | RE_PAGE := RE_SRC DOESN'T WORK !!! RE_SRC.Lines.SaveToStream(MS); MS.Position := 0; RE_PAGE.Lines.LoadFromStream(MS) ; // ... search placeholder l := RE_SRC.GetTextLen; p := RE_SRC.FindText('|NEWPAGE|', 0, l, [stMatchCase]); if (p > -1) then begin // ... found it RE_PAGE.Lines.BeginUpdate; // DELETE text after placeholder in print RE_PAGE.SelSTart := p; RE_PAGE.SelLength := l - p; RE_PAGE.SetSelText(''); RE_PAGE.Lines.EndUpdate; RE_SRC.Lines.BeginUpdate; // DELETE text upto/incl. placeholder in source RE_SRC.SelSTart := 0; RE_SRC.SelLength := p + 9; RE_SRC.SetSelText(''); RE_SRC.Lines.EndUpdate; RenderPage; //RenderRichEdit; // Render print-RTF PrintPreview.NewPage; // DON'T FORGET ! end; until (p < 0); MS.Free; RenderPage; //RenderRichEdit; // Render rest of / complete print-RTF end; PrintPreview.EndDoc; end; |
Zitat |
Registriert seit: 29. Mär 2008 Ort: D-49626 381 Beiträge Delphi 11 Alexandria |
#14
.... und noch ein UpDate:
Gestern beim herumspielen mit TPrintPreview (arbeitet mit TCustomRichEdit) ist mir plötzlich aufgefallen, das RichEdit offensichtlich doch einen Seitenumbruch beherscht - allerdings nur via Control '\page' und NICHT per '\pagebb'. Zur Erinnerung: ... ich wollte mit LibreOffice erstellte Seiten auch so wie generiert mit Seitenumbruch anzeigen/drucken. LO baut allerdings in den Plaintext ein PageBreakBefore ein. Keine Ahnung ob MS-Office oder andere Text-Proggies das auch so machen, ich hab z.Zt. kein anderes. Das vereinfacht die Sache natürlich nochmals. Man muss nun nach wie vor im Plaintext nach '\pagebb' suchen, braucht aber nichts einzubauen und wieder darauf zu testen o. ä., sondern einfach nur mit '\page' ersetzen. Ich kam drauf, weil ich eine meiner Test-Dateien schon manuell daraufhin editiert hatte...
Delphi-Quellcode:
Es fehlte übrigends noch der 'Seitenvorschub', wenn mehr als eine Datei aus der FileList gedruckt wurde.
// -----------------------------------------------------------------------------
// GLOBAL: FFrameRect, RichEdit procedure TMainForm.RenderRichEdit(Files : TStringList); var SS : TStringStream; RTFplain : ANSIString; // fails if wide string !!! i : integer; Offset : Integer; begin CalcFrame; // set print-rectangle with given borders PrintPreview.Units := mmHiMetric; // All units are in 1/100th of millimeter PrintPreview.BeginDoc; // ... loop the files for I := 1 to Files.Count do begin if (i > 1) then PrintPreview.NewPage; // new file -> new page // ----- check for PrintPageBefore SS := TStringStream.Create(''); // ... load file to stream and copy to ANSI-STRING !!! (NOT WIDE) SS.LoadFromFile(Files[i-1]); SetLength(RTFplain, SS.Size); SS.Read(RTFplain[1], SS.Size); // ... replace \pagebb with '\page' RTFplain := StringReplace(RTFplain, '\pagebb', '\page', [rfReplaceAll]); SS.Clear; SS.WriteBuffer(RTFplain[1],Length(RTFplain)); // load plain back to stream SS.Position := 0; RichEdit.Lines.LoadFromStream(SS); // load stream to RTF SS.Free; // ----- // render RTF (similar as in org. TPrintPreview 'RichTextDemo') Offset := 0; while (Offset >= 0) and (PrintPreview.PaintRichText(FFrameRect, RichEdit, 1, @Offset) <> 0) do begin Application.ProcessMessages; if (Offset >= 0) then PrintPreview.NewPage; end; end; PrintPreview.EndDoc; end; Geändert von TERWI (18. Dez 2017 um 17:07 Uhr) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |