Writer - Querverweis auf Überschriften

Programmierung unter AOO/LO (StarBasic, Python, Java, ...)

Moderator: Moderatoren

Opa Eckhard
Beiträge: 6
Registriert: Fr, 22.11.2019 12:36

Writer - Querverweis auf Überschriften

Beitrag von Opa Eckhard »

Hallo Freunde/Freundinnen,

meinen ersten Versuch habe ich wohl nicht im rechten Forum gestartet, hier bin ich hoffentlich nun richtig.

Arbeite mit LibreOffice Version: 6.0.7.3 unter macOS Catalina Version 10.15.1 im Writer-Modul und Base-Modul. Mittels Makro gelingt es mir aus den Daten einer Datenbank den Text eines umfangreicheren Writer-Dokumentes zu generieren. Der Text wird mit benutzerdefinierten Formatvorlagen formatiert (Überschriften in zwei Ebenen, Textkörper und Textkörper mit Einzug). Der Textkörper enthält Worte, die als als Querverweis auf eine Überschrift stehen. Ein Inhaltsverzeichnis ist nicht Bestandteil des Dokumentes.

Manuell gelingt das Einfügen des Querverweises auf eine Überschrift ohne Probleme. Der Viewcursor führt beim Anklicken des Verweistextes den erwarteten Sprung zur ausgewählten Überschrift sichtbar aus.

Da im Dokument aber mehr Querverweise der beschriebenen Art anzubringen sind, als manuell in vertretbarer Zeit zu bewältigen ist, will ich auch diesen Teil meiner Arbeit ein Makro erledigen lassen.

Andrew Pitonyak beschreibt in "BASIC-Makros für OpenOffice und LibreOffice" mit Listing 405. wie ein GetReference-Feld einzufügen ist. Mir gelingt dies in der Form

oField = oDoc.createInstance("com.sun.star.text.textfield.GetReference") oField.ReferenceFieldPart = com.sun.star.text.ReferenceFieldPart.TEXT oField.ReferenceFieldSource = com.sun.star.text.ReferenceFieldSource.BOOKMARK oField.SequenceNumber = oReferencedField.SequenceValue
oField.SourceName = sSeqName
oText.insertTextContent(oCurs, oField, True)

wenn ich für SequenceNumber und SourceName auslesbare Werte manuell eingefügter Querverweise benutze, um mein Makro zu testen. Woher nehme ich aber für alle anderen Querverweise die entsprechenden Werte?

Die als Sprungziel vorhandenen Überschriften erreiche ich zwar über oDoc.getLinks() kann aber die dort vermuteten Werte nicht finden.

Wer kann mir helfen? Danke.
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Writer - Querverweis auf Überschriften

Beitrag von Stephan »

wenn ich für SequenceNumber und SourceName auslesbare Werte manuell eingefügter Querverweise benutze, um mein Makro zu testen. Woher nehme ich aber für alle anderen Querverweise die entsprechenden Werte?
Ich fürchte Du machst einen Denkfehler, denn es gibt die Werte erst nachdem Du manuell einen Querverweis eingefügt hast, denn das Ziel eines Querverweises der auf eine Überschrift gesetzt wird ist eine (speziell benannte) Textmarke die LO erst beim Erzeugen des Querverweises automatisch einfügt.
WAs LO Dir im Dialog anzeigt sind tatsächlich NUR die Texte der Überschriften und falls es buchstabengleiche Überschriften gleicher Ebene gäbe/gibt ist die einzige Unterscheidung deren Reihenfolge die LO im Dialog genauso wiedergibt wie im Dokument vorhanden.

Ich habe das einmal getestet und hänge unten zwei Screenshots meines Testdokuments an, die das Gesagte verdeutlichen.

Ich weiß garnicht ob Du LO per Makro zwingen kannst 'seine' dafür verwendeten automatisch benannten Bookmarks (Textmarken) zu erzeugen und ich weiß auch nicht ob LO per Makro erzeugte speziell benannte Bookmarks adäquat behandeln würde, das müsste man mal testen.

Nötig ist das alles auch nicht, denn Du selbst kannst per Makro gezielt eigene Textmarken bei den Überschriften erzeugen und dann darauf Querverweise setzen. Um das zu tun gehst du so vor: mache eine Enumeration über den gesamten Text des Dokuments und prüfe ob der jeweils aktuelle Absatz die passende Absatzvorlage für Überschrift hat (das können wenn Du mehrere Überschriftenebenen mit Querverweisen anspringen willst auch mehrere sein) und wenn ja dann setze eine Textmarke passenden Namens, also ungefähr:

Code: Alles auswählen

cur = ThisComponent.text.createTextCursor

absaetze = ThisComponent.Text.createEnumeration()
Do While absaetze.hasMoreElements()
	akt_absatz = absaetze.nextElement()
	If akt_absatz.ParaStyleName <> "DeineÜberschriftenvorlage" Then
		a_index = a_index + 1
		oBookmark = tc.createInstance("com.sun.star.text.Bookmark")
		cur.gotoRange(akt_absatz.getAnchor, False)
		oBookmark.setName("xyz-" & a_index & "_" & cur.String)
		tc.Text.insertTextContent(cur, oBookmark, False)
	End If
Loop
nun hat jede benötigte Überschrift eine passende Textmarke und Du kennst die Namen (und den Namensaufbau) dieser Textmarken. Deshalb kannst Du nun darauf bequem Querverweise setzen, ungefähr so:

Code: Alles auswählen

tc = ThisComponent
oField = tc.createInstance("com.sun.star.text.textfield.GetReference")
oField.ReferenceFieldPart = com.sun.star.text.ReferenceFieldPart.PAGE
oField.ReferenceFieldSource = com.sun.star.text.ReferenceFieldSource.BOOKMARK
oField.SourceName = "Der Name der gewünschten Textmarke"
tc.text.insertTextContent(curx, oField, True)

Wie Du auf "Der Name der gewünschten Textmarke" zugreifst hängt von den Umständen ab, z.B. kannst Du beim Setzen der Textmarken, alle Namen in ein Array schreiben und das dann abarbeiten.
Der Vorteil der besonderen Benennung der Textmarken (s.o.) ist auch das Du weist welche Textmarke zu welcher Überschrift gehört und welche Textmarken im Gesamtdokument überhaupt die relevanten sind (alle deren Name vorne "xyz-" hat)




Gruß
Stephan
Dateianhänge
Querverweis_contentXML.gif
Querverweis_contentXML.gif (36.21 KiB) 2875 mal betrachtet
Querverweis_Bildschirmdarstellung.gif
Querverweis_Bildschirmdarstellung.gif (9.59 KiB) 2875 mal betrachtet
Opa Eckhard
Beiträge: 6
Registriert: Fr, 22.11.2019 12:36

Re: Writer - Querverweis auf Überschriften

Beitrag von Opa Eckhard »

Hallo Stephan,

danke für die rasche Antwort auf meine Frage. Deinen Lösungsvorschlag werde ich aufgreifen und testen. Ich bin zuversichtlich. Die Aufgabe bleibt übersichtlich, weil nur in einer Überschriftenebene die Verknüpfung hergestellt wird und die Texte der Überschriften frei von Duplikaten sind.

Es war dann wohl tatsächlich mein Irrtum anzunehmen, dass mit der Formatierung eines Textes als Überschrift zugleich auch die erwartete Einrichtung der Textmarke durch LO erfolgt, die ich leider nur vergeblich suchen konnte.
Stephan
********
Beiträge: 12369
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Writer - Querverweis auf Überschriften

Beitrag von Stephan »

Korrektur

statt:

Code: Alles auswählen

If akt_absatz.ParaStyleName <> "DeineÜberschriftenvorlage" Then
muss es natürlich heissen:

Code: Alles auswählen

If akt_absatz.ParaStyleName = "DeineÜberschriftenvorlage" Then
aber mein geposteter Code war ja ohnehin nur eine Lösungsskizze und nicht eine komplett fertige Lösung.


Gruß
Stephan
Opa Eckhard
Beiträge: 6
Registriert: Fr, 22.11.2019 12:36

Re: Writer - Querverweis auf Überschriften

Beitrag von Opa Eckhard »

Hallo Freunde,

rechtzeitig zum Weihnachtsfest hatte ich endlich die funktionierende Lösung im iMac. Das Ergebnis ist das erwartete Dokument mit Funktionalität. Auch der Export als PDF-Datei gelingt.

Es hat etwas länger gedauert, weil ich zunächst nicht erkannt hatte, dass die beiden Befehlszeile oZiel = ofbDoc.createInstance("com.sun.star.text.ReferenceMark") und oQuerverweis = ofbDoc.createInstance("com.sun.star.text.textfield.GetReference") für jedes Ziel und jeden Querverweis einzeln aufgerufen werden müssen, die Namen des Zieles nicht das Zeichen # enthalten dürfen und ein Querverweis nur eingefügt werden kann, wenn zuvor das Sprungziel eingefügt wurde.

Soweit mein vorläufiges Endergebnis, würde mich natürlich auch über ergänzende Hinweise zum Thema freuen. Mich interessiert noch die Frage, wie kann ich das Erscheinungsbild der Sprungziele und Querverweise im Dokument verändern, wenn die Standardeinstellungen nicht befriedigen.

Fröhliche Weihnacht und ein gesunden neues Jahr 2020 wünscht allen Freunden - Opa Eckhard

Code: Alles auswählen

sub SprungzieleEintragen
	REM Sprungziele zu den Familien eintragen
	'private ofbDoc as object
	'private oText as object
	dim oCur as object 'ein Textcursor
	dim oAbsText as string
	dim oZiel as object 'ein Sprungziel
	dim oZielName as string
	dim i as long 'Zählt die Anzahl der eingetragenen Sprungziele
	
	i = 0
	oCur = ofbDoc.text.createTextCursor()
	oCur.gotoStart(false) 'Bewegt den Cursor zum Anfang des Textes
	do
		'der Cursor steht am Anfang eines Absatzes und markiert den Absatz
		oCur.gotoEndOfParagraph(true)
		if oCur.ParaStyleName = "ofbÜberschrift 5" then
			oAbsText = oCur.String
			if oAbsText <> "#9999" then
				'ein manuell eingetragenes Sprungziel erhielt im Test den Text #9999
				'Sprungzielnamen dürfen das Zeichen # nicht enthalten
				i = i + 1
				oZielName = Right(oAbsText,4)
				oZiel = ofbDoc.createInstance("com.sun.star.text.ReferenceMark")
				oZiel.setName(oZielName)
				ofbDoc.Text.insertTextContent(oCur, oZiel, true)
			end if
		end if
	loop while oCur.gotoNextParagraph(false)
	ofbDoc.store()
end sub


sub QuerverweiseEintragen
	REM Das Dokument ofbDoc soll Textfelder enthalten der Art GetReference
	'berücksichtigt, dass ein Sprungziel aus mehreren Querverweisen erreicht werden kann
	'suchen nach regulärem Ausdruck "#...." im Dokument als Ort des einzufügenden Querverweises
	'private ofbDoc as object
	'private oText as object
	dim oQuerverweis as object
	dim oCur as object
	dim oSuche as object
	dim aktGefunden as object
	dim aktWort as string
	
	oText = ofbDoc.Text
	oCur = ofbDoc.Text.createTextCursor()
	oCur.gotoStart(false) 'Bewegt den Cursor zum Anfang des Textes
	
	oQuerverweis = ofbDoc.createInstance("com.sun.star.text.textfield.GetReference")
	
		oSuche = ofbDoc.createSearchDescriptor()
		With oSuche
			.SearchString = "#...." 'es fehlt ein Ausschluss der Überschriften im 
			.SearchRegularExpression = true
		end with
		'liefert die erste Fundstelle im Text, ungeachtet der Formatierung
		aktGefunden = ofbDoc.findFirst(oSuche)
		do while not isnull(aktGefunden)
			'nur an gefundenen Textstellen im Text aber nicht in Überschriften ein Querverweis-Objekt einfügen,
			'wenn dafür auch ein Sprungziel bereitsteht
			'Zeichenformat unterscheidet Überschriften vom Text
			aktWort = aktGefunden.String
			if aktGefunden.CharWeight = 150 then
				'Fundstelle ist Überschrift und Sprungziel
			else
				if aktGefunden.CharWeight = 100 then
					'100 steht für ofbEinzug Textkörper und ofbTextkörper
					'bei vollständigenm Text des OFB kann auf SprungzielTest verzichtet werden
					if SprungzieleTest(right(aktWort,4)) then
						'Sprungzieltest bestanden
						'einen Querverweis an Cursorposition einfügen, wo steht  der TextCursor ?
						oCur.gotoRange(aktGefunden,false) 'Cursor markiert den gesuchten Text
						oCur.collapseToStart()
						oCur.goRight(5,true)
						'ein einzelnes Querverweis-Objekt bereitstellen
						oQuerverweis = ofbDoc.createInstance("com.sun.star.text.textfield.GetReference")
						oQuerverweis.ReferenceFieldSource = com.sun.star.text.ReferenceFieldSource.REFERENCE_MARK
						oQuerverweis.ReferenceFieldPart = com.sun.star.text.ReferenceFieldPart.TEXT
						oQuerverweis.SequenceNumber = 0
						oQuerverweis.SourceName = right(aktWort,4)
						oQuerverweis.CurrentPresentation = aktWort
						oText.insertTextContent(oCur, oQuerverweis, true)
					else
						'Sprungzieltest nicht bestanden
					end if
				else
					'Formatvorgabe nicht erfüllt
				end if
			end if
			aktGefunden = ofbDoc.findNext(aktGefunden.End, oSuche)
		loop
		ofbDoc.store()
end sub

function SprungzieleTest(aktName as string) as boolean
	REM Querverweise benötigen reale Sprungziele
	'private ofbDoc as object
	dim oZiele as object 'alle Sprungziele
	dim nn()
	dim j as long
	oZiele = ofbDoc.getReferencemarks()
	nn = oZiele.ElementNames 'Array der Elementnamen
	j = 0
	SprungzieleTest = false
	for j = Lbound(nn()) to Ubound(nn())
		if nn(j) = aktName then SprungzieleTest = true
		if SprungzieleTest = true then exit for
	next
end function
Antworten