Ein Flow-Diagramm, das in ein PDF eingebettet wird, enthält keinen Text, sondern nur Vektorkonturen. Der Text lässt sich daher nicht markieren, kopieren oder durchsuchen (Kundenbeschwerde).
Analyse
Das Diagramm wird nicht gerastert - der Weg FlowFactory.toSvg → <img src="data:image/svg+xml;base64,..."> → pdfFile → SVGReplacedElementFactory zeichnet über PdfTemplate.createGraphics(...), also über iTexts PdfGraphics2D, das PDF-Zeichenoperatoren schreibt.
Verloren geht der Text eine Ebene darüber: Batiks voreingestellter StrokingTextPainter füllt die Glyphenkonturen, statt Graphics2D.drawString aufzurufen. Beim Graphics2D kommt also nie Text an.
Nachgewiesen mit PDFBox am erzeugten PDF:
page xobject Xf1: PDFormXObject extracted text:
Weder auf der Seite noch im Form-XObject ist eine Schrift eingebettet, die Textextraktion liefert nichts.
Achtung bei der Prüfung: pdf2txt ist als Nachweis ungeeignet. Die Funktion weicht auf OCR aus (pdftoppm + tesseract) und liefert den Text eines aus Konturen gezeichneten Diagramms genauso wie den eines aus echtem Text gezeichneten. Nur das Auslesen der Textoperatoren unterscheidet beides.
Lösung
SVG bleibt die Zwischenform; korrigiert wird die Stelle, die tatsächlich Text verliert.
SelectableTextPainter zeichnet einen Textlauf über Graphics2D.drawString(String, float, float), statt seine Glyphenkonturen zu füllen. Auf dem Graphics2D eines PdfTemplate schreibt drawString Textoperatoren und lässt die Schrift einbetten. Installiert wird der Painter auf dem BridgeContext, mit dem SVGReplacedElementFactory das Dokument rendert.
Damit gilt die Korrektur für jedes in ein PDF eingebettete SVG, nicht nur für Flow-Diagramme, und keiner der beiden Diagramm-Stacks muss angepasst werden.
Nicht jeder Lauf ist als eine an einer Position gezeichnete Zeichenkette darstellbar. Ein Lauf an einem Pfad, ein vertikal oder von rechts nach links gesetzter, ein Lauf mit einzeln positionierten Glyphen und ein mit Kontur oder ungleichmäßiger Füllung gezeichneter behalten ihre Konturdarstellung - ebenso ein Lauf, dessen Text nicht dort landen würde, wo das Layout ihn vorgesehen hat. Solche Läufe übernimmt weiterhin Batiks StrokingTextPainter, ihr Erscheinungsbild ändert sich nicht.
Die Schrift wird auf die Basisschrift Helvetica abgebildet, statt eine Schriftdatei einzubetten. Das ist metrisch verlustfrei, weil die Schriftdeklaration der Diagramme (Arial, 'Liberation Sans', Arimo, Helvetica, sans-serif) metrikkompatible Vertreter nennt: Die für den Text reservierte Breite und die gezeichnete stimmen überein.
Nebenbefund: fehlende Größe
Ohne width/`height` am <img> wird das Diagramm gar nicht gezeichnet, das PDF bleibt leer. DiagramOperations.draw schreibt immer width="100%" height="100%", ein ersetztes Element hat damit keine intrinsische Größe.
SVGReplacedElementFactory greift jetzt auf die Ausdehnung der viewBox zurück, wenn am <img> keine Größe steht. Eine relative width/`height` wie 100% beschreibt, wie ein Dokument den ihm gegebenen Platz füllt, nicht wie viel Platz es anfordert.
Test
test.com.top_logic.graphic.flow.server.script.TestFlowPdf erzeugt ein PDF auf dem Produktionsweg und prüft, dass das Diagramm gezeichnet wird und dass sein Text auch Text ist - einmal mit Größe am <img> und einmal ohne.