Calc Makro: Rätselhafte Zeilenhöhe

Ich füge per Makro eine Grafik in ein Tabellenblatt ein. Die Grafik soll in der Höhe bündig in eine gewisse Anzahl von Zeilen eingepasst sein. Dabei erlebe ich eine Diskrepanz zwischen tatsächlicher und gewünschter Zeilenhöhe und zwischen tatsächlicher Gesamthöhe und dem Produkt von Zeilenhöhe und Zeilenanzahl.

Die Zeilenhöhe sei 0,35 cm, die Zeilenanzahl sei iRows = 150.

   oSheet.getCellRangeByPosition(2, 1, 2, iRows).Rows.Height = 350

Die Gesamthöhe wäre dann rechnerisch 52500.
Tatsächlich aber sind es nur 52387, gemessen in der DrawPage:

   lTop = oSheet.getCellByPosition(0, 1).Position.Y
   lBottom = oSheet.getCellByPosition(0, iRows + 1).Position.Y
   lHeight = lBottom - lTop

Ich stelle fest, dass das Tabellenblatt-GUI zwar eine Zeilenhöhe von 0,35 zeigt, diese aber in Wirklichkeit einen Hundertstel mm niedriger ist, nämlich 349:

   oSheet.getCellByPosition(0, 1).Rows.Height

Das läppert sich. Was aber noch rätselhafter ist, dass somit das Produkt aus tatsächlicher Zeilenhöhe und Zeilenanzahl 52350 ist, aber nicht die echten 52387. Da ich durchaus mit einer deutlich höheren Zeilenanzahl rechnen muss, wird der Unterschied schnell auch sichtbar.

Wie sind diese Unterschiede zu erklären?

Schöne Grüße
Volker

Rundungsfehler; /du/ rechnest mit dem Dezimalsystem, der /Rechner/
dagegen mit dem Binärsystem. Aber die wenigsten Dezimalbrüche lassen
sich auch im Binärformat exakt darstellen. So ist z. B. die binäre
Darstellung des dezimalen Wertes 0,1 ein Wert, der dezimal tatsächlich
0.10000000000000000555111512312578270211815834045... darstellt. Macht im
Normalfall wenig aus, kann aber in Extremfällen bei sehr genauer
Betrachtung zu geringfügigen Differenzen führen. Und durch
Multiplikationen potenzieren sich die Differenzen noch.

Ich würde dir daher empfehlen (mach ich auch immer), Grafiken nicht
/bündig/ ein zu fügen, sondern immer mit Einzug auf allen Seiten (aka
überall ein bisschen Abstand lassen).

Wolfgang

Das läppert sich. Was aber noch rätselhafter ist, dass somit das Produkt
aus tatsächlicher Zeilenhöhe und Zeilenanzahl 52350 ist, aber nicht die
echten 52387. Da ich durchaus mit einer deutlich höheren Zeilenanzahl
rechnen muss, wird der Unterschied schnell auch sichtbar.

Wie sind diese Unterschiede zu erklären?

Rundungsfehler; /du/ rechnest mit dem Dezimalsystem, der /Rechner/
dagegen mit dem Binärsystem. Aber die wenigsten Dezimalbrüche lassen
sich auch im Binärformat exakt darstellen. So ist z. B. die binäre
Darstellung des dezimalen Wertes 0,1 ein Wert, der dezimal tatsächlich
0.10000000000000000555111512312578270211815834045... darstellt. Macht im
Normalfall wenig aus, kann aber in Extremfällen bei sehr genauer
Betrachtung zu geringfügigen Differenzen führen. Und durch
Multiplikationen potenzieren sich die Differenzen noch.

Danke Wolfgang, für die Erinnerung ans Binärsystem. Dennoch scheint es mir nicht nutzerfreundlich zu sein, die Einstellungen für z.B. die Zeilenhöhe in Hundertstel mm, Format Long, für die Ein- und Ausgabe festzulegen, diese aber bei der Eingabe abzurunden, z.B. auf 349/350 . Das ist weit mehr als die Rundungsdifferenz. Den Rundungsfehler kann ich bei der Multiplikation der tatsächlichen Zeilenhöhe mit der Zeilenanzahl nachvollziehen.

Ich würde dir daher empfehlen (mach ich auch immer), Grafiken nicht
/bündig/ ein zu fügen, sondern immer mit Einzug auf allen Seiten (aka
überall ein bisschen Abstand lassen).

Im Übrigen erhalte ich durch die Abstandmessung der Y-Positionen die absolut genaue Höhe. Diese Methode ist einfach und effektiv. Es ging mir nur um Klarheit.

Das Problem eines fehlenden Abstands der eigentlichen Grafik von den unterliegenden Folienrändern fällt erst dann auf, wenn die einzelnen Seiten einer über mehrere Seiten gehenden Grafik gedruckt werden. Ist ein Abstand vorhanden, und sei er noch so gering, ist alles gut: der entsprechende Teil der Grafik erscheint nur innerhalb der Zeilen. Ohne Abstand jedoch oder nach Abschneiden der Ränder wird der entsprechende Teil der Grafik auf der gesamten Seite, d.h. über die Zeilengrenzen hinaus gedruckt und auch ins PDF-Format exportiert. Die Druckvorschau sieht hingegen ganz manierlich aus: Grafik nur innerhalb der Zeilen. Das mag ein Bug sein.

Schöne Grüße
Volker

Danke Wolfgang, für die Erinnerung ans Binärsystem. Dennoch scheint es
mir nicht nutzerfreundlich zu sein, die Einstellungen für z.B. die
Zeilenhöhe in Hundertstel mm, Format Long, für die Ein- und Ausgabe
festzulegen,

Hättest Du lieber Angaben in Pixel oder Punkte o. ä. statt mm?

diese aber bei der Eingabe abzurunden, z.B. auf 349/350 .

Erstens ist das nicht "350" sondern "1/350"; und es wird auch nicht
absichtlich o. ä. abgerundet sondern das ist lediglich die beste
*Annäherung* an den betreffenden Dezimalwert. Mehr kann man IMHO nicht
verlangen.

Wenn du beim Obsthändler 1 kg Tomaten kaufst, bekommst du auch nicht
immer [tm] exakt 1.000,000000000000 g. Und wenn du 350 solcher Käufe
zusammenwirfst, erhältst du auch einen ganz schön satten
'Rundungsfehler'. :wink:

Das ist weit mehr als die Rundungsdifferenz.

In den Wert spielen noch eine ganze Reihe weiterer Faktoren hinein, z.
B. die Monitor-/Druckerauflösung, die alle mit ihren eigenen
Rundungsfehlern kommen, was das ganze und so exponentiell wirken.

Wolfgang

Hättest Du lieber Angaben in Pixel oder Punkte o. ä. statt mm?

Natürlich nicht, aber ich bin da irgendwie komisch. Wenn ich eine Ganzzahl eingebe, so erwarte ich auch, dass dieselbe Ganzzahl gespeichert wird. Aus weit entfernten Assemblerzeiten erinnere ich mich an entsprechende Befehle :slight_smile:

... es wird auch nicht
absichtlich o. ä. abgerundet sondern das ist lediglich die beste
*Annäherung* an den betreffenden Dezimalwert. Mehr kann man IMHO nicht
verlangen.

Wenn du beim Obsthändler 1 kg Tomaten kaufst, bekommst du auch nicht
immer [tm] exakt 1.000,000000000000 g. Und wenn du 350 solcher Käufe
zusammenwirfst, erhältst du auch einen ganz schön satten
'Rundungsfehler'. :wink:

Sicherlich. Daher geht es mir ja gar nicht wirklich um die Berechnung der Gesamthöhe aller Zeilen (die kann ich - wie schon erwähnt - über die Differenz der Y-Positionen exakt ermitteln), sondern darum, dass ich nicht einen Double-Wert von 350.0 als Zeilenhöhe eingebe, sondern exakt 350. Wenn intern, was ich natürlich verstehe, der Wert binär etwas kleiner ist, so wird er doch als niedrigere Ganzzahl verwendet, denn beim Auslesen des Werts wirkt nicht etwa die umgekehrte Logik. Um in deinem Beispiel zu bleiben: Wenn ich eine Tomate kaufe, und das zehnmal, ärgere ich mich, wenn es nur 9 sind.

Ich habe das Ganze einmal durchgespielt. Die tatsächliche Zeilenhöhe variiert im Bereich von 1 unter dem Eingabewert bis 1 darüber.

Die Zeilenhöhe spielt für mich insofern eine Rolle, dass ich vermeiden möchte, dass beim Ausdruck der Gesamtgrafik auf der letzten Seite nur ganz wenige Zeilen stehen. Daher berechne ich Zeilenanzahl pro Seite und erhöhe bzw. vermindere die Zeilenhöhe so, dass die letzte Seite nicht ganz so verloren aussieht. (Die Zeilenanzahl, die Grafikdatei und weitere Parameter werden über einen Dialog ermittelt.)

Ich habe nun gelernt, dass ich nach dem Setzen der Zeilenhöhe die tatsächliche Höhe auslesen muss. Die Differenz beim Vergleich mit der Seitenhöhe ist dann so gering, dass man damit leben kann.

Ganz simpel: iRowsPerPage = lPageHeight \ lRealRowHeight
Und: iRowsOnLastPage = iRows Mod iRowsPerPage
Oder was wäre besser?

Schöne Grüße
Volker

Hättest Du lieber Angaben in Pixel oder Punkte o. ä. statt mm?

Natürlich nicht, aber ich bin da irgendwie komisch. Wenn ich eine
Ganzzahl eingebe, so erwarte ich auch, dass dieselbe Ganzzahl
gespeichert wird.

0,35 cm /ist/ aber keine Ganzzahl; weder in Millimeter noch in Inch.

Wenn du beim Obsthändler 1 kg Tomaten kaufst, bekommst du auch nicht
immer [tm] exakt 1.000,000000000000 g. Und wenn du 350 solcher Käufe
zusammenwirfst, erhältst du auch einen ganz schön satten
'Rundungsfehler'. :wink:

Sicherlich. Daher geht es mir ja gar nicht wirklich um die Berechnung
der Gesamthöhe aller Zeilen (die kann ich - wie schon erwähnt - über die
Differenz der Y-Positionen exakt ermitteln), sondern darum, dass ich
nicht einen Double-Wert von 350.0 als Zeilenhöhe eingebe, sondern exakt
350.

Nein, du gibst "0,35 cm" ein. Das ist keine Ganzzahl.

Wenn intern, was ich natürlich verstehe, der Wert binär etwas
kleiner ist, so wird er doch als niedrigere Ganzzahl verwendet, denn
beim Auslesen des Werts wirkt nicht etwa die umgekehrte Logik.

Doch, natürlich; da wird genauso (nur umgekehrt) gerundet, und dir
wieder "0,35 cm" angezeigt.

Um in
deinem Beispiel zu bleiben: Wenn ich eine Tomate kaufe, und das zehnmal,
ärgere ich mich, wenn es nur 9 sind.

Nein, es sind 10; aber es sind 10 Kleine, die nur das Gewicht von 9
Großen haben.

Ich habe das Ganze einmal durchgespielt. Die tatsächliche Zeilenhöhe
variiert im Bereich von 1 unter dem Eingabewert bis 1 darüber.

Die Zeilenhöhe spielt für mich insofern eine Rolle, dass ich vermeiden
möchte, dass beim Ausdruck der Gesamtgrafik auf der letzten Seite nur
ganz wenige Zeilen stehen. Daher berechne ich Zeilenanzahl pro Seite und
erhöhe bzw. vermindere die Zeilenhöhe so, dass die letzte Seite nicht
ganz so verloren aussieht. (Die Zeilenanzahl, die Grafikdatei und
weitere Parameter werden über einen Dialog ermittelt.)

Ich habe nun gelernt, dass ich nach dem Setzen der Zeilenhöhe die
tatsächliche Höhe auslesen muss. Die Differenz beim Vergleich mit der
Seitenhöhe ist dann so gering, dass man damit leben kann.

Moment mal; im OP ging es dir darum ein Bild o. ä. exakt in x Zeilen ein
zu passen. Was hat das mit der Seitenhöhe oder der Anzahl Zeilen pro
Seite zu tun?

Ganz simpel: iRowsPerPage = lPageHeight \ lRealRowHeight
Und: iRowsOnLastPage = iRows Mod iRowsPerPage
Oder was wäre besser?

Ich würde lPageHeight um einen minimalen Betrag (z. B. 0,00001 cm o. ä.)
ab- und lRealRowHeight entsprechend aufrunden (oder einfach von der
Division selbst vor dem Abrunden einen winzigen Betrag fix abziehen), um
möglicherweise bereits hier vorhandene Rundungsfehler sozusagen auf die
sichere Seite zu schieben. Andernfalls könnte es im Extremfall passieren
(wenn nämlich die beiden Werte unglücklich zur falschen Seite gerundet
werden, und das Ergebnis auch noch ganz knapp bei einer runden Zahl
liegt), dass du als Ergebnis der Division statt z. B. einem (korrekten)
2,999999999 ein (falsches) 3,000000001 o. ä. bekommst; ansonsten kann
ich so auf die Schnelle kein nennenswertes Problem entdecken.

Aber was hat das mit deinem eingangs angesprochenen Problem zu tun?

Wolfgang

Es geht ... darum, dass ich
nicht einen Double-Wert von 350.0 als Zeilenhöhe eingebe, sondern exakt
350.

Nein, du gibst "0,35 cm" ein. Das ist keine Ganzzahl.

Dass das GUI 0,35 anzeigt, ist gut und richtig, nämlich benutzerfreundlich. Mir geht es aber um die Höhenfestlegung per Makro, und die geschieht mit einer Ganzzahl. Zitat aus der IDL reference:

Dim lRowHeight As Long 'com::sun::star::table::TableRow
                        'Public Attribute: long Height
                        'contains the height of the row (in 1/100 mm).

Wenn dieser Wert intern wieder in einen Gleitkommawert gewandelt wird, so hätte man doch als Eingabe gleich den Typ Double festlegen können.

Hoppla, jetzt beim Schreiben beschleicht mich der Verdacht, dass die IDL überall nur Typ Long vorsieht :-/
Ich muss das mal überprüfen.

Die Zeilenhöhe spielt für mich insofern eine Rolle, dass ich vermeiden
möchte, dass beim Ausdruck der Gesamtgrafik auf der letzten Seite nur
ganz wenige Zeilen stehen. Daher berechne ich Zeilenanzahl pro Seite und
erhöhe bzw. vermindere die Zeilenhöhe so, dass die letzte Seite nicht
ganz so verloren aussieht. (Die Zeilenanzahl, die Grafikdatei und
weitere Parameter werden über einen Dialog ermittelt.)

Ich habe nun gelernt, dass ich nach dem Setzen der Zeilenhöhe die
tatsächliche Höhe auslesen muss. Die Differenz beim Vergleich mit der
Seitenhöhe ist dann so gering, dass man damit leben kann.

Moment mal; im OP ging es dir darum ein Bild o. ä. exakt in x Zeilen ein
zu passen. Was hat das mit der Seitenhöhe oder der Anzahl Zeilen pro
Seite zu tun?

Tut mir Leid, dass da ein Missverständnis entstehen konnte. Ich hatte im OP gezeigt, wie ich die tatsächliche Gesamthöhe der Zeilen ermittele, nämlich über die Y-Positionen. Mein erster Ansatz war allerdings die Multiplikation der Zeilenanzahl mit der Zeilenhöhe gewesen, der dann leider nicht das gewünschte Ergebnis brachte. Ich bat um eine Erklärung für die Differenz.

Dass ich die Zeilenhöhe auch zur Berechnung der Zeilen pro Seite benötige, hätte ich allerdings schon im OP erwähnen können.

Ganz simpel: iRowsPerPage = lPageHeight \ lRealRowHeight
Und: iRowsOnLastPage = iRows Mod iRowsPerPage
Oder was wäre besser?

Ich würde lPageHeight um einen minimalen Betrag (z. B. 0,00001 cm o. ä.)
ab- und lRealRowHeight entsprechend aufrunden (oder einfach von der
Division selbst vor dem Abrunden einen winzigen Betrag fix abziehen), um
möglicherweise bereits hier vorhandene Rundungsfehler sozusagen auf die
sichere Seite zu schieben. Andernfalls könnte es im Extremfall passieren
(wenn nämlich die beiden Werte unglücklich zur falschen Seite gerundet
werden, und das Ergebnis auch noch ganz knapp bei einer runden Zahl
liegt), dass du als Ergebnis der Division statt z. B. einem (korrekten)
2,999999999 ein (falsches) 3,000000001 o. ä. bekommst; ansonsten kann
ich so auf die Schnelle kein nennenswertes Problem entdecken.

Werde ich testen. Danke für den Tipp.

Schöne Grüße
Volker

Hei Volker,

nur um die Diskussion "noch ein wenig" akademischer zu gestalten;)

Dass das GUI 0,35 anzeigt, ist gut und richtig, nämlich benutzerfreundlich. Mir geht es aber um die Höhenfestlegung per Makro, und die geschieht mit einer Ganzzahl. Zitat aus der IDL reference:

Dim lRowHeight As Long 'com::sun::star::table::TableRow
                       'Public Attribute: long Height
                       'contains the height of the row (in 1/100 mm).

Wenn dieser Wert intern wieder in einen Gleitkommawert gewandelt wird, so hätte man doch als Eingabe gleich den Typ Double festlegen können.

Auch das ist alles "nur" benutzerfreundlich. Intern wird voraussichtlich nicht einmal mit diesen Werten gerechnet, sondern eher mit "ma" oder "twips" - also bildschirm- und auflösungsabhängigen Variablen. Das könnte man vielleicht im Quellcode finden. D.h. aber, auch die long-Werte werden immer noch umgerechnet.. mit den bekannten Problemen und Ungenauigkeiten.

Das heisst: Auch die für den Makro-Code angegebene Einheit (long: hunderstel MIllimeter) hilft beim Rendern wenig. Die wird mit Sicherheit umgerechnet.
Im Ergebnis ist auch der Seitenumbruch und die Tabellen (Zeilen-) Höhe nur ein Ergebnis des Rendering-Prozesses - und der wird sicher nicht in 100stel mm berechnet.

Aber wie gesagt... hier könnte Dir nur ein Blick in den Quellcode helfen. Ich kann und weiss es leider nicht genau.

VG
Thomas

Danke Thomas und Wolfgang. Das ist alles nachvollziehbar. Für das Problem der Zeilen pro Seite bzw. die Frage, wo der Seitenumbruch stattfindet, mag es vielleicht noch andere Lösungen geben. Ich werde die Frage gesondert posten.

Schöne Grüße
Volker

Hallo Volker,

ich mische mich erst verspätet in die Diskussion ein, in späteren Mails als der, auf die ich diese Antwort schicke, ist schon vieles gesagt, insbesondere Thomas Krumbeins Hinweis auf "ma" oder "twips" - findest du auch etwas ausführlicher in seinem Buch über Makros - bringt den eigentlichen Kern des Problems herein, nämlich die Notwendigkeit der Umrechnung in geräteabhängige Maßeinheiten. Aber die Mail, auf die ich antworte, ist für mich der Kern der Fragestellung.
Die Gleichsetzung der Eingabe eines ganzahligen Wertes mit dem abstrakten Konzept einer Ganzzahl ist nicht schon nicht ganz ohne. Das sieht so aus, weil die zugehörige Maßeinheit z.B. 1/100 mm ist, aber ist das dann eine saubere Ganzzahl? In welchem Kontext? Ganze Zahlen kommen meiner Meinung nach vom Zählen, und von da zum Messen von Abständen, wofür du ja die Gannzahl verwendest, ist ein weiter Weg. Bedenke: wir sind hier mit praktischen Dingen beschäftigt und nicht mit Theorie (ich bin selbst Mathematiker!), und man muss immer den Kontext bedenken. Du könntest sagen, es sei eine Dezimalzahl mit z.B. 3 fixen Nachkommastellen, das sei fast das gleiche. Das Problem ist, dass du diese Zahl meist mit einer anderen in Beziehung setzen musst (schon wenn du z.B. eine Tabelle mit drei Spalten auf einer Seite darstellen willst, da hast du gleich ein Verhältnis mit einem unendlichen, wenn auch periodischen Dezimalbruch, auch im Binärsystem, und den kann man nicht mit endlich vielen Nachkommastellen darstellen, die es einfach braucht wenn man ein allgemein verwendbares System bereitstellen will), und damit bist du sebst bei der vereinfachenden Annahme, dass alles im metrischen System läuft, schon beim Problem von Rundungen und den daraus folgenden Ungenauigkeiten, mit denen du rechnen musst.  Mag sein, dass man ein geschicktes System für die Behandlung von Brüchen mit EDV entwerfen könnte, aber dann brauchen wir ja auch mal Quadratwurzeln und sonstige algebraische Zahlen und mit Sinus u.ä. auch transzendente Zahlen, und dann landen wir pragmatisch wieder bei Annäherungen durch endliche Brüche.
Hinzukommen die Berücksichtigung unterschiedlicher Maßsysteme (Zoll!) und die schon genannten Geräte-spezifischen Anforderungen.
Die Programmierer müssen all das berücksichtigen, und wenn sie das mit Double-Variablen tun, nützen sie den Typ, der die höchste Präzision hergibt. Das funktioniert ja auch annähernd bzw. ziemlich genau, und wer mehr will, muss sich selber bemühen. Du weißt das ja auch und tust das entsprechend, mir geht es nur darum, ein Verständnis für die Schwierigkeiten zu wecken, die in dem Thema stecken. Dabei ist das auch nur eine Außenansicht, ich bin in diesen Themen nicht aktiv, ich versuche nur, die Prinzipien nachzuvollziehen.

Gruß

Gerhard

Hallo Gerhard,

herzlichen Dank für den ausführlichen Kommentar zu den Eingabewerten bei Größenangaben. Ich hätte es wissen oder zumindest argwöhnen müssen, dass intern möglicherweise mit Twips gerechnet wird. Aber es war für mich eine Art Schock, nach einer Eingabe von
oSheet.getCellByPosition(0, 1).Rows.Height = 350
die Ausgabe
oSheet.getCellByPosition(0, 1).Rows.Height '349
zu finden, die ich zuerst gar nicht auf dem Schirm hatte.

Vielleicht bin ich ja nicht der einzige, der zu unbedarft mit diesen Werten umgeht, zumal in den allermeisten Fällen eine Differenz von einem Hundertstel Millimeter völlig unerheblich ist. So mag dieser Dialog dem einen oder anderen eine Hilfe sein.

Schöne Grüße
Volker