FPVectorial, SVG title und desc

Rund um die LCL und andere Komponenten
Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Hallo Ihr Lieben,
ich versuche mich gerade darin eine kleine Anwendung zu basteln, die eine SVG-Datei schreiben soll.
Das Beispiel aus https://wiki.freepascal.org/fpvectorial#SVG habe ich zum Laufen bekommen.
Jetzt sagt die W3 SVG Spezifikation, dass jedes Element bei Bedarf ein "title"- und/oder ein "desc"-Element haben darf. Diese beiden Elemente werden nicht dargestellt, sondern dienen zur inneren Erklärung, können aber von geeigneten Programmen als "Tooltip" dargestellt werden, wenn ein entsprechendes Element (wie auch immer) ausgewählt wird.
Wird ein entsprechendes SVG-Dokument z.B. in einen Browser geladen und fährt man mit der Maus über ein Element welches einen "title" enthält, wird der Inhalt als kleines Textfeld angezeigt.
Technisch wird das Ganze als XML-Element innerhalb einer Gruppe realisiert.

Ausgehend von dem Beispiel aus der o.a. Seite sähe das so aus:

Code: Alles auswählen

<g>
  <title>Text in english</title>
  <text 
    x="35.433"
    y="318.897"
    font-family="Arial,Helvetica,sans-serif"
    font-size="35.43"
    fill="#000000"
  >10,10 Some text in english.</text>
</g>
Das eigentliche Text-Element (Ausgabe "10,10 Some text in english.") wird in eine Gruppe gekapselt und innerhalb der Gruppe gibt es ein paralleles "title"-Element, dessen Inhalt (Ausgabe "Text in english") im Tooltip dargestellt wird.

Leider gibt es innerhalb der Unit fpvectorial keine Elemente die diese Optionen bieten und ich bin ziemlich unschlüssig wo das eingebaut werden könnte.
Ein Weg mit der Brechstange ginge vielleicht so:
Alle geschriebenen Elemente in procedure TvSVGVectorialWriter.WriteEntity(...) (in Datei svgvectorialwriter.pas, Zeile 374) sind von TvNamedEntity abgeleitet. Diese Klasse verfügt über eine public Property "Name" von Typ String, welche nirgends verwendet wird.
In procedure TvSVGVectorialWriter.WriteEntity(...) könnte eine einfache Abfrage über Length(TvNamedEntity(AEntity).Name) > 0 ein umschließendes "<g>"-Element erzeugen und ein zusätzliches "<title>%s</title>" einfügen, mit %s ersetzt mit Name.

Sauberer wäre natürlich die Klasse TvEntity um eine Property "Desc" und "Title" zu erweitern, das würde aber dann Änderungen in zwei Units erfordern.
Eine schöne Lösung ohne Änderung in den Units sehe ich nicht, es fehlen dazu leider passende Events in procedure TvSVGVectorialWriter.WriteEntity(...), wie bspw. "OnBeforeWritingEntity" und "OnAfterWritingEntity" oder "OnWritingEntity".

Aber vielleicht stehe ich ja auf dem Schlauch und jemand hilft mir freundlich über die Straße.
Grüße aus Hildesheim
Ekkehard

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Leider hat sich der Entwickler von fpVectorial verabschiedet, und so dass das Package nur noch Support aus der zweiten Linie erhält...

Mein Eindruck beim Überfliegen von fpVectorial.pas ist, dass "Name" durchaus verwendet wird, denn in TvPage und TvVectorialPage gibt es eine Methode "FindEntityWidthNameAndType"; dieselbe Methode gibt es in TvEntityWithSubEntities, von dem TvBlock, TvLayer, TvParagraph, TvList und TvRichText abgeleitet sind. Ich meine, dass der Name so ähnlich verwendet wird wie der Namen von Komponenten im Objektinspektor und würde daher davon abraten, das Feld für andere Zwecke zu verwenden, auch wenn es vom SVG-Writer explizit nicht verwendet wird. Ich denke, wenn du diese Popups wirklich brauchst, führt kein Weg daran vorbei, ein neues Feld in der Klassenhierarchie einzubauen.

Die Frage ist: lohnt sich das? Was willst du überhaupt machen? Brauchst du wirklich fpVectorial dafür? Evtl ist es einfacher, die xml-Dateien (nichts anderes ist svg) von Hand zu erzeugen, als in einer komplexen für eine sehr allgemeine Anwendung ausgelegten Klassenhierarchie herumzustochern.

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Vielen Dank für die Antwort.
Evtl ist es einfacher, die xml-Dateien (nichts anderes ist svg) von Hand zu erzeugen
Das wäre natürlich die einfachste Idee, ich dachte allerdings eher andersherum, vielleicht gibt es sowas schon fertig und ich kann es (mit wenigen Veränderungen) einfach verwenden,
Am Ende läuft es dann wohl auf einen "privaten" Fork hinaus.
Leider hat sich der Entwickler von fpVectorial verabschiedet,
Ich befürchtete das schon ein wenig. Ein Projekt dessen letzte Änderung schon 8 Jahre zurück liegt, ist entweder trivial und fertig oder tot,
Gibt es denn irgendwo eine "offizielle" Variante die noch gepflegt wird und wenn ja wo und von wem?
Was willst du überhaupt machen?
Ich wollte automatisch einen ganzen Schwung Grafiken erzeugen (für Wikpedia) und hätte bisher einfach Bitmaps/PNGs generiert. Da aber in den Grafiken keinerlei Rasterdaten vorkommen, dachte ich, ich könnte die Gelegenheit nutzen und mich endlich mal mit SVG beschäftigen und die Dateien entsprechend so erzeugen. Aber wie bei den Bitmaps, wo man idR nicht in den Pixeln direkt rumhantiert, sondern die Funktionen der Canvas benutzt, dachte ich es müsste etwas geben, was das auch für eine SVG-Darstellung tut und landete bei fpvectorial.
Grundsätzlich geht das schon (auch wenn das Beispiel selber kleine Macken hat), jetzt wollte ich die Chance nutzen mittels Title und Desc weitere Informationen hinzufügen, die das maschinelle Durchsuchen der erzeugten SVG erleichtert und bspw. Links zu den Quellen der einzelnen Elemente enthält.

Und ganz grundsätzlich wollte ich vermeiden irgendwo dran rumzubasteln und dann kommt die Community und sagt "Alte Version" oder "Nimm doch ABCMachtAlles"...
Grüße aus Hildesheim
Ekkehard

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Ekkehard hat geschrieben: Fr 10. Mai 2024, 12:28
Leider hat sich der Entwickler von fpVectorial verabschiedet,
Ich befürchtete das schon ein wenig. Ein Projekt dessen letzte Änderung schon 8 Jahre zurück liegt, ist entweder trivial und fertig oder tot,
Gibt es denn irgendwo eine "offizielle" Variante die noch gepflegt wird und wenn ja wo und von wem?
Nachdem ich vor einigen Jahren im SVG-Teil ein paar Änderungen eingebracht habe, wird das Projekt zur Zeit im wesentlichen von mir selbst betreut. FPVectorial wird zusammen mit Lazarus ausgeliefert. Die letzte Änderung war im April dieses Jahres - von wegen 8 Jahre! Ein "privater" Fork ist nicht nötig. Wenn deine Änderungen ins Konzept passen und nichts kaputt machen, dann kommen sie ins Package. Das ist Open Source...
Ich wollte automatisch einen ganzen Schwung Grafiken erzeugen (für Wikpedia) und hätte bisher einfach Bitmaps/PNGs generiert. Da aber in den Grafiken keinerlei Rasterdaten vorkommen, dachte ich, ich könnte die Gelegenheit nutzen und mich endlich mal mit SVG beschäftigen und die Dateien entsprechend so erzeugen.
Dafür ist aber FPVectorial nicht das richtige Tool. Es ist kein Graphik-Design-Tool, mit dem du SVG-Graphiken zeichnen kannst. Viel leichter machst du das mit einen echten Zeichenprogramm, etwa Inkscape, das primär SVG-Graphiken erzeugt und mit dem etwa User Ally die allermeisten Icons erstellt hat, die Lazarus zur Zeit verwendet.

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Nachdem ich vor einigen Jahren im SVG-Teil ein paar Änderungen eingebracht habe, wird das Projekt zur Zeit im wesentlichen von mir selbst betreut. FPVectorial wird zusammen mit Lazarus ausgeliefert. Die letzte Änderung war im April dieses Jahres - von wegen 8 Jahre!
Alle Links zur Quelle auf https://wiki.freepascal.org/fpvectorial sind tot oder führen irgendwohin wo man nichts findet.
Was ich gefunden habe war das https://github.com/alrieckert/lazarus/t ... pvectorial und das Zeug ist 8 Jahre alt.

Bitte schreibe doch hier einen Link zur aktuellen Quelle.

Ich würde ggf ein paar Vorschläge machen und Dir zur Begutachtung geben.
Dafür ist aber FPVectorial nicht das richtige Tool. Es ist kein Graphik-Design-Tool, mit dem du SVG-Graphiken zeichnen kannst.
Ich will nicht eine Zeichnung machen (dafür ist Inkscape natürlich die bessere Wahl), sondern etwa 50, die alle etwas unterschiedlich aussehen, weil sie auf unterschiedlichen Daten basieren. Konkret sieht das Ergebnis ungefähr so aus:
test.png
(290.47 KiB) Noch nie heruntergeladen

es fehlt noch die Legende, Titel etc.
Die Beispielgrafik stellt einen Kalender über 200 Jahre von 1901 bis 2100 dar, jede Zeile umfasst ein Jahr. Die Markierungen sind Sonnenfinsternisse. In der Art wird es sehr viele geben, mit unterschiedlichen Zeiträumen (Alle Jahrunderte von -2000 bis +3000) und Markierungen. Das malt man nicht einfach mit Inkscape und macht dabei Tausende Übertragungsfehler in den Daten :-). Genau dafür ist mMn Lazarus (und möglicherweise FPVectorial) das richtige Tool.

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Ekkehard hat geschrieben: Fr 10. Mai 2024, 19:57 Alle Links zur Quelle auf https://wiki.freepascal.org/fpvectorial sind tot oder führen irgendwohin wo man nichts findet.
Ah, da steckt noch das alte svn-Repository drin...
Ekkehard hat geschrieben: Fr 10. Mai 2024, 19:57 Bitte schreibe doch hier einen Link zur aktuellen Quelle.
Du brauchst keinen. FPVectorial ist in der Lazarus-Installation enthalten, und das schon seit vielen Versionen: components/fpvectorial.
Ekkehard hat geschrieben: Fr 10. Mai 2024, 19:57 Ich würde ggf ein paar Vorschläge machen und Dir zur Begutachtung geben.
Gerne. Du solltest aber auf der Lazarus-Main Version aufbauen. Also installiere dir Lazarus/main direkt aus dem GitLab repository heraus, oder unter Mithilfe von fpcupdeluxe. Oder gehe auf diese Seite https://gitlab.com/freepascal.org/lazar ... type=heads, klicke auf "Code" und wähle unter "Dieses Verzeichnis herunterladen" zip oder eine der anderen Optionen.
Ich will nicht eine Zeichnung machen (dafür ist Inkscape natürlich die bessere Wahl), sondern etwa 50, die alle etwas unterschiedlich aussehen, weil sie auf unterschiedlichen Daten basieren. Konkret sieht das Ergebnis ungefähr so aus:
test.png
es fehlt noch die Legende, Titel etc.
Die Beispielgrafik stellt einen Kalender über 200 Jahre von 1901 bis 2100 dar, jede Zeile umfasst ein Jahr. Die Markierungen sind Sonnenfinsternisse. In der Art wird es sehr viele geben, mit unterschiedlichen Zeiträumen (Alle Jahrunderte von -2000 bis +3000) und Markierungen. Das malt man nicht einfach mit Inkscape und macht dabei Tausende Übertragungsfehler in den Daten :-). Genau dafür ist mMn Lazarus (und möglicherweise FPVectorial) das richtige Tool.
Da hast du recht. Aber trotzdem nochmal die Anregung: Um Linien zu ziehen und Kreise zu zeichnen und dabei deine Zusatzfelder einzubauen, erscheint es mir einfacher, den svg-Text selbst zu schreiben als in die komplexe Klassenhierarchie von fpVectorial einzugreifen, was auch für alle anderen unterstützten Graphik-Formate funktionieren muss.

Eine ganz andere Vorgehensweise wäre TAChart zu verwenden - einen Datenpunkt pro Sonnenfinsternis, und die Series geschickt aufbauen, so dass deine Darstellung entsteht. Und TAChart kann auch nach SVG exportieren. Einschränkend vielleicht aber die Bemerkung, dass das, glaube ich, die title-Elemente auch nicht ohne weiteres unterstützt.

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 594
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: FPVectorial, SVG title und desc

Beitrag von Niesi »

Ekkehard hat geschrieben: Fr 10. Mai 2024, 19:57
Die Beispielgrafik stellt einen Kalender über 200 Jahre von 1901 bis 2100 dar, jede Zeile umfasst ein Jahr. Die Markierungen sind Sonnenfinsternisse. In der Art wird es sehr viele geben, mit unterschiedlichen Zeiträumen (Alle Jahrunderte von -2000 bis +3000) und Markierungen. Das malt man nicht einfach mit Inkscape und macht dabei Tausende Übertragungsfehler in den Daten :-). Genau dafür ist mMn Lazarus (und möglicherweise FPVectorial) das richtige Tool.


Du solltest Dir die Anregung von wp_xyz einmal genau überlegen - ich denke, er hat da den besseren Weg angeregt. Deine test.png dürfte mit svg-Text recht einfach zu beschreiben sein. Hier mal der svg-Text einer einfachen Zeichnung aus Inkscape:


Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   width="297mm"
   height="210mm"
   viewBox="0 0 297 210"
   version="1.1"
   id="svg1"
   inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
   sodipodi:docname="Zeichnung.svg"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview
     id="namedview1"
     pagecolor="#ffffff"
     bordercolor="#000000"
     borderopacity="0.25"
     inkscape:showpageshadow="2"
     inkscape:pageopacity="0.0"
     inkscape:pagecheckerboard="0"
     inkscape:deskcolor="#d1d1d1"
     inkscape:document-units="mm"
     inkscape:zoom="1.0307169"
     inkscape:cx="561.25984"
     inkscape:cy="397.29629"
     inkscape:window-width="1920"
     inkscape:window-height="1048"
     inkscape:window-x="1920"
     inkscape:window-y="0"
     inkscape:window-maximized="1"
     inkscape:current-layer="layer1" />
  <defs
     id="defs1" />
  <g
     inkscape:label="Ebene 1"
     inkscape:groupmode="layer"
     id="layer1">
    <circle
       style="fill:#d40000;stroke:#f9f9f9;stroke-width:0;stroke-linejoin:round"
       id="path1"
       cx="71"
       cy="71"
       r="21" />
    <rect
       style="fill:#0000ff;stroke:#f9f9f9;stroke-width:0;stroke-linejoin:round"
       id="rect1"
       width="42"
       height="42"
       x="100"
       y="100" />
    <text
       xml:space="preserve"
       style="font-size:6.35001px;font-family:'Liberation Sans';-inkscape-font-specification:'Liberation Sans';text-align:center;text-anchor:middle;fill:#0000ff;stroke:#f9f9f9;stroke-width:0;stroke-linejoin:round"
       x="58.476463"
       y="25.601318"
       id="text1"><tspan
         sodipodi:role="line"
         id="tspan1"
         style="stroke-width:0"
         x="58.476463"
         y="25.601318">Sample text</tspan></text>
  </g>
</svg>
Bildschirmfoto vom 2024-05-11 07-44-48.png
Bildschirmfoto vom 2024-05-11 07-44-48.png (28.36 KiB) 592 mal betrachtet


Da besteht die Möglichkeit, den Background der Zeichnung mit Inkscape zu erstellen und im Programm zu verwenden, anschließend kannst Du systematisch die Kreise, Texte und was auch immer einfügen.
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Deine test.png dürfte mit svg-Text recht einfach zu beschreiben sein. Hier mal der svg-Text einer einfachen Zeichnung aus Inkscape
Ich fühle mich gerade wie in einem Fahrradladen, wo der Verkäufer mir erklärt, wie ich eine Busfahrkarte kaufen kann ;-)
Im Ernst. Das PNG habe ich mit Lazarus erzeugt, das sind vielleicht 100 Zeilen mit Canvas.Pen.Color, Canvas.Ellipse etc. Anweisungen, die das machen. Das sollte doch möglich sein mit den Methoden von TvVectorialPage in eine SVG zu übersetzen und zu speichern. Warum soll ich jetzt mit Inkscape anfangen?

Die einzige Frage die ich hatte (und das war der Anlass dieses Threads) ob ich einfach die gewünschten Ergänzungen von "Title" und "Descr" vornehmen kann. Die mir selbst gegebene Antwort lautet, "Ja geht, weil Open Source und Du den Quelltext ändern kannst". Gehört hätte ich gerne "Ja geht, und das ohne Veränderung des Quelltextes, weil Ergänzungen durch Verwenden von abgeleiteten Klassen mit überschriebenen Methoden oder der Verwendung von Ereignissen möglich ist".
Um von Antwort 1 zu Antwort 2 zu gelangen, werde ich Vorschläge machen.
Einer folgt hier:

Eine einzige klitzekleine Änderung dürfte das für den Anfang schon tun:

Code: Alles auswählen

unit svgvectorialwriter;
(...)
  TvSVGVectorialWriter = class(TvCustomVectorialWriter)    
  (...)
  protected 
    procedure WriteEntity(AStrings: TStrings; ADoc: TvVectorialDocument;
      APage: TvVectorialPage; AEntity: TvEntity);virtual;
Die Methode TvSVGVectorialWriter.WriteEntity aus private entfernen und in einen neue protected Bereich verschieben und mit virtual zu einer virtuellen Methode zu machen, die es erlaubt die überschriebenen Methoden von abgeleiteten Klassen aufzurufen.
Diese Änderung ist vollkommen kompatibel zu allen Anwendungen.

Man könnte noch mehr machen und alle Methoden zum Schreiben der Enities virtualisieren, denn wer weiß, vielleich will mal einer eine Ellipse anders schreiben.

Was auch noch schön wäre, wäre eine Eselei in der unit fpvectorial zu entfernen:

Code: Alles auswählen

unit fpvectorial;     
(...)
procedure RegisterVectorialWriter(
  AWriterClass: TvVectorialWriterClass;
  AFormat: TvVectorialFormat);
(...)
    if GvVectorialFormats[i].Format = AFormat then
    begin
      if GvVectorialFormats[i].WriterRegistered then // Diese und die nächste Zeile können ersatzlos weg!
       raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
(...)


Warum wird diese Exception erzeugt, wenn da eine Klasse eingetragen ist?
Das zugehörige Array GvVectorialFormats ist global und aus jeder Ecke der Welt kann da drin rum gemurkst werden!
Dabei ist doch genau das die Ecke, wo man eine abgeleitete neue WriterKlasse eintragen würde, damit in

Code: Alles auswählen

procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
var
  AWriter: TvCustomVectorialWriter;
begin
  AWriter := CreateVectorialWriter(AFormat);
  try
    AWriter.WriteToFile(AFileName, Self);
  finally
    AWriter.Free;
  end;
end;
der entsprechende Writer erzeugt wird. Aber gut, diese drei Zeilen kann man auch selber noch basteln.
LG Ekkehard

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Ekkehard hat geschrieben: Sa 11. Mai 2024, 09:52 Dabei ist doch genau das die Ecke, wo man eine abgeleitete neue WriterKlasse eintragen würde, damit in

Code: Alles auswählen

procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
var
  AWriter: TvCustomVectorialWriter;
begin
  AWriter := CreateVectorialWriter(AFormat);
  try
    AWriter.WriteToFile(AFileName, Self);
  finally
    AWriter.Free;
  end;
end;
der entsprechende Writer erzeugt wird.
Ich hab's jetzt anders realisiert (und in Lazarus/main-Repository hochgeladen). Ähnlich wie bei FCL-Image gibt es jetzt überladene Read und Write-Methoden, in denen man eine eigenen Reader/Writer angeben kann. Das hat den Vorteil, dass man einen selbst-geschriebenen Writer (und auch Reader) verwenden kann, ohne diesen registrieren zu müssen und damit in den Fehler wegen des schon vorhandenen Writers zu laufen. Es können sogar der Original-Writer und der eigene Writer gemeinsam verwendet werden.

Code: Alles auswählen

var
  Vec: TvVectorialDocument;
  writer: TMySVGVectorialWriter;
...
    vec.WriteToFile('svgwritetest-orig.svg'); // Diese Datei wird vom Original-Writer geschrieben

    writer := TMySVGVectorialWriter.Create;
    try
      Vec.WriteToFile('svgwritetest-new.svg', writer);   // ... diese vom eigenen.
    finally
      writer.Free;
    end; 
Im beigefügten Projekt wird das demonstriert (du brauchst dazu das FPVectorial aus der aktuellen Lazarus/main-Version). Zur Vereinfachung habe ich die svgvectorialwriter unit genommen, umbenannt und in dem Kommentar in der zweiten svg-Zeile den Zusatz "MySVGVectorialWriter" angebracht, so dass man im Text-Editor erkennen kann, welcher Writer verwendet wurde.

Ekkehard hat geschrieben: Sa 11. Mai 2024, 09:52 Was auch noch schön wäre, wäre eine Eselei in der unit fpvectorial zu entfernen:

Code: Alles auswählen

procedure RegisterVectorialWriter(
  AWriterClass: TvVectorialWriterClass;
  AFormat: TvVectorialFormat);
(...)
    if GvVectorialFormats[i].Format = AFormat then
    begin
      if GvVectorialFormats[i].WriterRegistered then // Diese und die nächste Zeile können ersatzlos weg!
       raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
(...)
Ich gebe dir recht: das macht es unmöglich, für ein Format einen anderen Writer zu registrieren. Ich habe es aber noch etwas weiter vereinfacht und committet.

Code: Alles auswählen

{@@
  Registers a new writer for a format

  An already-registered writer is replaced.
}
procedure RegisterVectorialWriter(
  AWriterClass: TvVectorialWriterClass;
  AFormat: TvVectorialFormat);
var
  i, len: Integer;
begin
  len := Length(GvVectorialFormats);

  { First search for the format in the list }
  for i := 0 to len - 1 do
  begin
    if GvVectorialFormats[i].Format = AFormat then
    begin
      { If found, register the writer class. }
      GvVectorialFormats[i].WriterClass := AWriterClass;
      Exit;
    end;
  end;

  { If not already in the list, then add it }
  SetLength(GvVectorialFormats, len + 1);
  GvVectorialFormats[len].Format := AFormat;
  GvVectorialFormats[len].ReaderClass := nil;
  GvVectorialFormats[len].WriterClass := AWriterClass;
end; 
Analog auch beim Reader (bei dem die Exception-Zeile eh schon auskommentiert war).
Nun aber nochmal zu dem Vorschlag, die svg-Datei direkt zu schreiben. Ich habe mir dafür eine kleine Klasse TSVGCanvas gemacht, in der man mit Hilfe einiger vom LCL-Canvas her bekannter Befehle direkt in die svg-Datei schreiben kann, ohne den Umweg über FPVectorial - siehe Anhang "SVGCanvas".
Dateianhänge
fpvectorial_customwriter.zip
(8.21 KiB) 24-mal heruntergeladen
svgcanvas.zip
(2.79 KiB) 24-mal heruntergeladen

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Besten Dank, ich werde die Sachen mal ausprobieren.
Leider hast Du die Methode WriteEntity nicht protected und virtual gestellt, so dass man alles doch selber komplett neu basteln muss und nicht nur die eine Methode überschreiben kann. Klassen zu bauen ohne relevante Detailmethoden zu virtualisieren schränkt die Wiederverwendbarkeit bestehender Klassen massiv ein.

Code: Alles auswählen

procedure TvSVGVectorialWriter.WriteEntity(AStrings: TStrings;
  ADoc: TvVectorialDocument; APage: TvVectorialPage; AEntity: TvEntity);
begin
  if AEntity is TPath then
    WritePath(AStrings, ADoc, APage, TPath(AEntity))
  else
  if AEntity is TvText then
    WriteText(AStrings, ADoc, APage, TvText(AEntity))
  else
  if AEntity is TvCircle then
    WriteCircle(AStrings, ADoc, APage, TvCircle(AEntity))
  else
  if AEntity is TvEllipse then
    WriteEllipse(AStrings, ADoc, APage, TvEllipse(AEntity))
  else
  if AEntity is TvRectangle then
    WriteRectangle(AStrings, ADoc, APage, TvRectangle(AEntity))
  else
  if AEntity is TvPolygon then
    WritePolygon(AStrings, ADoc, APage, TvPolygon(AEntity))
  else
  if AEntity is TvLayer then
    WriteLayer(AStrings, ADoc, APage, TvLayer(AEntity))
  else
  if AEntity is TvParagraph then
    WriteParagraph(AStrings, ADoc, APage, TvParagraph(AEntity));
end;
Allein schon die Tatsache, dass es hier eine offene Liste gibt, legt nahe, dass es dazu Erweiterungsbedarf geben könnte. Deshalb virtual, damit jemand das überschreiben kann um neue Enities zu integrieren.
LG Ekkehard

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Ekkehard hat geschrieben: Sa 11. Mai 2024, 19:56 Besten Dank, ich werde die Sachen mal ausprobieren.
Leider hast Du die Methode WriteEntity nicht protected und virtual gestellt, so dass man alles doch selber komplett neu basteln muss und nicht nur die eine Methode überschreiben kann. Klassen zu bauen ohne relevante Detailmethoden zu virtualisieren schränkt die Wiederverwendbarkeit bestehender Klassen massiv ein.

Code: Alles auswählen

procedure TvSVGVectorialWriter.WriteEntity(AStrings: TStrings;
  ADoc: TvVectorialDocument; APage: TvVectorialPage; AEntity: TvEntity);
begin
  if AEntity is TPath then
    WritePath(AStrings, ADoc, APage, TPath(AEntity))
  else
  if AEntity is TvText then
    WriteText(AStrings, ADoc, APage, TvText(AEntity))
  else
  if AEntity is TvCircle then
    WriteCircle(AStrings, ADoc, APage, TvCircle(AEntity))
  else
  if AEntity is TvEllipse then
    WriteEllipse(AStrings, ADoc, APage, TvEllipse(AEntity))
  else
  if AEntity is TvRectangle then
    WriteRectangle(AStrings, ADoc, APage, TvRectangle(AEntity))
  else
  if AEntity is TvPolygon then
    WritePolygon(AStrings, ADoc, APage, TvPolygon(AEntity))
  else
  if AEntity is TvLayer then
    WriteLayer(AStrings, ADoc, APage, TvLayer(AEntity))
  else
  if AEntity is TvParagraph then
    WriteParagraph(AStrings, ADoc, APage, TvParagraph(AEntity));
end;
Allein schon die Tatsache, dass es hier eine offene Liste gibt, legt nahe, dass es dazu Erweiterungsbedarf geben könnte. Deshalb virtual, damit jemand das überschreiben kann um neue Enities zu integrieren.
LG Ekkehard
Nein, mit WriteEntity kommst du nicht weiter. Du musst weiter in die Tiefe gehen. Angenommen du willst einen Kreis um den Title erweitern. Dann musst du dir WriteCircle ansehen und auch dieses auf virtual schalten:

Code: Alles auswählen

procedure TMySVGVectorialWriter.WriteCircle(AStrings: TStrings;
  ADoc: TvVectorialDocument; APage: TvVectorialPage; ACircle: TvCircle);
var
  cx: Double = 0.0;
  cy: Double = 0.0;
  cr: Double = 0.0;
  dtmp: double = 0.0;
  circleStr: string;
begin
  ConvertFPVCoordinatesToSVGCoordinates(APage, ACircle.X, ACircle.Y, cx, cy);
  ConvertFPVSizeToSVGSize(ACircle.Radius, 0, cr, dtmp);
  circleStr := Format('  <circle cx="%g" cy="%g" r="%g" style="%s %s" />', [
    cx, cy, cr,
    GetPenAsXMLStyle(ACircle.Pen),
    GetBrushAsXMLStyle(ACircle.Brush)
    ], FPointSeparator);
  AStrings.Add(circleStr);
end; 
Wie willst du hier in die '<circle>'-Anweisung einen zusätzlichen Node für den '<title>' reinbringen - das ist doch alles hart-codiert. Gut, du könntest die Methode 1:1 in den neuen Writer reinkopieren, so dass du '<circle><title>...</title></circle>' schreiben kannst. Aber das wird nicht kompilieren, denn ConvertFPVCoordinatesToSVGCoordinates ist private. Also müsste ich auch das sichtbarer machen, und ConvertFPVSizeToSVGSize, und und und... Ich weiß nicht, was sich Felipe gedacht hat, das alles so zu verrammeln, einen Grund wird er schon gehabt haben.

Leider ist FPVectorial sehr stark gekapselt, man kann das sicher besser machen, aber dafür muss man sich Zeit nehmen, und die habe ich momentan nicht.

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Guten Morgen,
Leider ist FPVectorial sehr stark gekapselt, man kann das sicher besser machen, aber dafür muss man sich Zeit nehmen, und die habe ich momentan nicht.
vielleicht bin ich etwas fundamental rüber gekommen, dafür bitte ich um Entschuldigung. Du weißt sicher besser wofür Du Deine Zeit einsetzen kannst, da will ich (und wollte ich nicht) irgendetwas insistieren.

Der Kern meiner Frage ist aber beantwortet ab jetzt wird es philosophisch.
Ich fasse für alle nochmal zusammen was ich jetzt mache und warum das mit der aktuellen Variante so nicht geht,
Ich erkläre die WriteEntity-Methode des TvSVGVectorialWriter zu virtual und verschiebe sie in einen neuen protected Bereich.

Code: Alles auswählen

  protected
    procedure WriteEntity(AStrings: TStrings; ADoc: TvVectorialDocument;
      APage: TvVectorialPage; AEntity: TvEntity);virtual;  
Ich leite eine Klasse des Writers ab und überschreibe genau diese eine Methode.

Code: Alles auswählen

  TvSVGVectorialWriterEx = class(TvSVGVectorialWriter)
  private
    procedure WriteEntity(AStrings: TStrings; ADoc: TvVectorialDocument;
      APage: TvVectorialPage; AEntity: TvEntity);override;
  end;


Diese Methode prüft ob in der Property Name der Entity was drin steht (das kann ich machen, weil ich ansonsten die Property nicht verwende, (man könnte alternativ und sauberer auch eine externe StringList aufbauen und die entities im Objects-Feld speichern) und baut einen <g></g>-Block um die Entity herum.

Code: Alles auswählen

procedure TvSVGVectorialWriterEx.WriteEntity(AStrings: TStrings;
  ADoc: TvVectorialDocument; APage: TvVectorialPage; AEntity: TvEntity);
var
  lHasTitle : Boolean = False;
begin
  // Adding Title in block, if desired
  lHasTitle := (AEntity is TvNamedEntity) and
               (Length(TvNamedEntity(AEntity).Name) > 0);
  if lHasTitle then
  begin
    AStrings.Add('  <g>');
    AStrings.Add(Format('  <title>%s</title>',[TvNamedEntity(AEntity).Name]));
  end;
  try
    inherited WriteEntity(AStrings,ADoc,APage,AEntity);
  // Ending block for title
  finally
    if lHasTitle then
      AStrings.Add('  </g>');
  end;
end;
Zur eigentlichen Ausgabe der Entity wird die WriteEntity-Methode des originalen Writers aufgerufen.
Fertig ist die Laube :-)
Nur geht das Ganze eben nur, wenn die WriteEntity-Methode in der Ursprungsklasse auf virtual gestellt ist, denn sonst wird ja meine überschriebene Methode nicht von WriteEntities aus aufgerufen,

Code: Alles auswählen

procedure TvSVGVectorialWriter.WriteEntities(AStrings: TStrings;
  ADoc: TvVectorialDocument; APage: TvVectorialPage);
var
  lEntity: TvEntity;
  i: Integer;
begin
  for i := 0 to APage.GetEntitiesCount() - 1 do
  begin
    lEntity := APage.GetEntity(i);
    WriteEntity(AStrings, ADoc, APage, lEntity);  // Ist WriteEntity nicht "virtual", kann keine abgeleitete Methode aufgerufen werden!!
  end;
end;  
Damit soll es das von meiner Seite gewesen sein. Ich wünsche einen schönen Sonntag!
LG Ekkehard

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 594
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: FPVectorial, SVG title und desc

Beitrag von Niesi »

wp_xyz hat geschrieben: Sa 11. Mai 2024, 18:05
...

Nun aber nochmal zu dem Vorschlag, die svg-Datei direkt zu schreiben. Ich habe mir dafür eine kleine Klasse TSVGCanvas gemacht, in der man mit Hilfe einiger vom LCL-Canvas her bekannter Befehle direkt in die svg-Datei schreiben kann, ohne den Umweg über FPVectorial - siehe Anhang "SVGCanvas".


Die beiden Beispiele habe ich mal ausprobiert, Danke dafür. SvgCanvas macht das, was auch ich meinte - wäre das nicht über BGRaBitmap nicht auch zu erreichen?

Im "svgwritetest" gibt es leider einen Compilerfehler:

Bildschirmfoto vom 2024-05-12 09-14-55.png
Bildschirmfoto vom 2024-05-12 09-14-55.png (76.04 KiB) 500 mal betrachtet
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

Ekkehard
Beiträge: 67
Registriert: So 12. Feb 2023, 12:42
OS, Lazarus, FPC: Windows Lazarus 3.6, FPC 3.2.2
CPU-Target: 64-Bit
Wohnort: Hildesheim

Re: FPVectorial, SVG title und desc

Beitrag von Ekkehard »

Im "svgwritetest" gibt es leider einen Compilerfehler:
Das liegt vermutlich daran, dass die Komponente aus dem original Verzeichnis von Lazarus eingebunden wird und nicht die aktuelle, von wp_xyz aktualisierte Version.
Diese Version findet sich hier: https://gitlab.com/freepascal.org/lazar ... pvectorial.
Da wird dann

Code: Alles auswählen

procedure WriteToFile(AFileName: String; AWriter: TvCustomVectorialWriter); overload;
deklariert, dann kompiliert der Spaß.
LG Ekkehard

wp_xyz
Beiträge: 5167
Registriert: Fr 8. Apr 2011, 09:01

Re: FPVectorial, SVG title und desc

Beitrag von wp_xyz »

Ekkehard hat geschrieben: So 12. Mai 2024, 08:50 Ich fasse für alle nochmal zusammen was ich jetzt mache
Warum nicht gleich so? Jetzt habe ich es verstanden und WriteEntity protected und virtual gemacht.

Antworten