latex3 / latex2e

The LaTeX2e kernel
https://www.latex-project.org/
LaTeX Project Public License v1.3c
1.96k stars 267 forks source link

rotation in right to left contexts #241

Open davidcarlisle opened 4 years ago

davidcarlisle commented 4 years ago

Brief outline of the bug

this is essentially a copy of

https://github.com/latex3/babel/issues/42

but restricted to the core rotation issues.

\documentclass{article}

\usepackage{graphics}

\begin{document}

x\fbox{\rotatebox{90}{abc}}

\ifx\textdir\undefined\else
\textdir TRT 
\fi

\ifx\beginR\undefined\else
\TeXXeTstate=1
\leavevmode\beginR
\fi
x\fbox{\rotatebox{90}{abc}}y

\end{document}

In luatex you get

image

in xetex you get

image

and in pdftex you get

pdfTeX warning: pdflatex: \pdfrestore: missing \pdfsave

!pdfTeX error: pdflatex: 1 unmatched \pdfsave after page shipout
 ==> Fatal error occurred, no output PDF file produced!
davidcarlisle commented 4 years ago

It is rather under-specified whether the default centre of rotation is the left hand edge or the right hand edge (actually it is specified in several places that it is the left, but it is implicitly implying left-to-right writing direction)

If we assume that the centre should be the start of the baseline in the writing direction then \rotatebox{90}{..} will come mostly below the baseline in a RTL context. In luatex this I think gives the desired output if we take that interpretation,

image

\documentclass{article}

\usepackage{graphicx}

\makeatletter
\protected\long\def\STDrotatebox#1#2{%
  \leavevmode
  \Grot@setangle{#1}%
  \setbox\z@\hbox{{#2}}%
\ifnum\textdirection=\z@
  \Grot@x\z@
\else
  \Grot@x\wd\z@
\fi
  \Grot@y\z@
  \Grot@box}

\long\def\Grot@box@std#1#2{%
  \Grot@setangle{#1}%
  \setbox\z@\hbox{{#2}}%
\ifnum\textdirection=\z@
  \Grot@x\z@
\else
  \Grot@x\wd\z@
\fi
  \Grot@y\z@
  \Grot@box}
\long\def\Grot@box@kv[#1]#2#3{%
  \@begin@tempboxa\hbox{#3}%
    \Grot@x\width \divide\Grot@x\tw@
    \Grot@y\height \advance\Grot@y-\depth \divide\Grot@y\tw@
    \setkeys{Grot}{#1}%
    \setbox\z@\box\@tempboxa
    \Grot@setangle{#2}%
    \Grot@box
  \@end@tempboxa}

\def\Grot@box{%
  \begingroup
  \CalculateSin\Grot@angle
  \CalculateCos\Grot@angle
  \edef\Grot@sin{\UseSin\Grot@angle}%
  \edef\Grot@cos{\UseCos\Grot@angle}%
%   \GDebug{Rotate: angle \Grot@angle, sine is \Grot@sin,
%             cosine is \Grot@cos}%
  \Grot@r\wd\z@  \advance\Grot@r-\Grot@x
  \Grot@l\z@     \advance\Grot@l-\Grot@x
  \Grot@h\ht\z@  \advance\Grot@h-\Grot@y
  \Grot@d-\dp\z@ \advance\Grot@d-\Grot@y
  \ifdim\Grot@sin\p@>\z@
    \ifdim\Grot@cos\p@>\z@
      \Grot@Py\Grot@height \Grot@r\Grot@h%B
      \Grot@Px\Grot@right  \Grot@r\Grot@d%E
      \Grot@Px\Grot@left   \Grot@l\Grot@h%C
      \Grot@Py\Grot@depth  \Grot@l\Grot@d%D
    \else
      \Grot@Py\Grot@height \Grot@r\Grot@d%E
      \Grot@Px\Grot@right  \Grot@l\Grot@d%D
      \Grot@Px\Grot@left   \Grot@r\Grot@h%B
      \Grot@Py\Grot@depth  \Grot@l\Grot@h%C
    \fi
  \else
    \ifdim\Grot@cos\p@<\z@
      \Grot@Py\Grot@height \Grot@l\Grot@d%D
      \Grot@Px\Grot@right  \Grot@l\Grot@h%C
      \Grot@Px\Grot@left   \Grot@r\Grot@d%E
      \Grot@Py\Grot@depth  \Grot@r\Grot@h%B
    \else
      \Grot@Py\Grot@height \Grot@l\Grot@h%C
      \Grot@Px\Grot@right  \Grot@r\Grot@h%B
      \Grot@Px\Grot@left   \Grot@l\Grot@d%D
      \Grot@Py\Grot@depth  \Grot@r\Grot@d%E
    \fi
  \fi
  \advance\Grot@height\Grot@y
  \advance\Grot@depth\Grot@y
  \Grot@Px\dimen@  \Grot@x\Grot@y
  \Grot@Py\dimen@ii \Grot@x\Grot@y
  \dimen@-\dimen@     \advance\dimen@-\Grot@left
  \dimen@ii-\dimen@ii \advance\dimen@ii\Grot@y
%   \GDebug{Rotate: (l,r,h,d)^^J%
% Original \the\Grot@l,\the\Grot@r,\the\Grot@h,\the\Grot@d,^^J%
% New..... \the\Grot@left,\the\Grot@right,%
%          \the\Grot@height,\the\Grot@depth}%
  \setbox\z@\hbox{%
    \kern\dimen@
    \raise\dimen@ii\hbox{%
\ifnum\textdirection=\z@\else
\typeout{[\the\Grot@left][\the\Grot@right][\the\dimen@]}%
\kern\dimexpr\Grot@right-\dimen@\relax
\raise-\dimen@ii\hbox
\fi
{\Grot@start\box\z@\Grot@end}}}%
  \ht\z@\Grot@height
  \dp\z@-\Grot@depth
  \advance\Grot@right-\Grot@left\wd\z@\Grot@right
  \leavevmode\box\z@
  \endgroup}

\makeatother

\begin{document}

a\fbox{\rotatebox[origin=Br]{90}{abc}}

1\fbox{\rotatebox{45}{abc}}
2\fbox{\rotatebox{90}{abc}}
3\fbox{\rotatebox{-45}{abc}}
4\fbox{\rotatebox{-90}{abc}}

\ifx\textdir\undefined\else
\textdir TRT 
\fi

a\fbox{\rotatebox[origin=Br]{90}{abc}}

1\fbox{\rotatebox{45}{abc}}
2\fbox{\rotatebox{90}{abc}}
3\fbox{\rotatebox{-45}{abc}}
4\fbox{\rotatebox{-90}{abc}}

\end{document}
u-fischer commented 4 years ago

Origin = Bl doesn't look right. And perhaps one would need origins s and e for start and end in writing direction (so s=l in TLT and r in TRT).

image

\documentclass{article}

\usepackage{graphicx}

\makeatletter
\protected\long\def\STDrotatebox#1#2{%
  \leavevmode
  \Grot@setangle{#1}%
  \setbox\z@\hbox{{#2}}%
\ifnum\textdirection=\z@
  \Grot@x\z@
\else
  \Grot@x\wd\z@
\fi
  \Grot@y\z@
  \Grot@box}

\long\def\Grot@box@std#1#2{%
  \Grot@setangle{#1}%
  \setbox\z@\hbox{{#2}}%
\ifnum\textdirection=\z@
  \Grot@x\z@
\else
  \Grot@x\wd\z@
\fi
  \Grot@y\z@
  \Grot@box}
\long\def\Grot@box@kv[#1]#2#3{%
  \@begin@tempboxa\hbox{#3}%
    \Grot@x\width \divide\Grot@x\tw@
    \Grot@y\height \advance\Grot@y-\depth \divide\Grot@y\tw@
    \setkeys{Grot}{#1}%
    \setbox\z@\box\@tempboxa
    \Grot@setangle{#2}%
    \Grot@box
  \@end@tempboxa}

\def\Grot@box{%
  \begingroup
  \CalculateSin\Grot@angle
  \CalculateCos\Grot@angle
  \edef\Grot@sin{\UseSin\Grot@angle}%
  \edef\Grot@cos{\UseCos\Grot@angle}%
%   \GDebug{Rotate: angle \Grot@angle, sine is \Grot@sin,
%             cosine is \Grot@cos}%
  \Grot@r\wd\z@  \advance\Grot@r-\Grot@x
  \Grot@l\z@     \advance\Grot@l-\Grot@x
  \Grot@h\ht\z@  \advance\Grot@h-\Grot@y
  \Grot@d-\dp\z@ \advance\Grot@d-\Grot@y
  \ifdim\Grot@sin\p@>\z@
    \ifdim\Grot@cos\p@>\z@
      \Grot@Py\Grot@height \Grot@r\Grot@h%B
      \Grot@Px\Grot@right  \Grot@r\Grot@d%E
      \Grot@Px\Grot@left   \Grot@l\Grot@h%C
      \Grot@Py\Grot@depth  \Grot@l\Grot@d%D
    \else
      \Grot@Py\Grot@height \Grot@r\Grot@d%E
      \Grot@Px\Grot@right  \Grot@l\Grot@d%D
      \Grot@Px\Grot@left   \Grot@r\Grot@h%B
      \Grot@Py\Grot@depth  \Grot@l\Grot@h%C
    \fi
  \else
    \ifdim\Grot@cos\p@<\z@
      \Grot@Py\Grot@height \Grot@l\Grot@d%D
      \Grot@Px\Grot@right  \Grot@l\Grot@h%C
      \Grot@Px\Grot@left   \Grot@r\Grot@d%E
      \Grot@Py\Grot@depth  \Grot@r\Grot@h%B
    \else
      \Grot@Py\Grot@height \Grot@l\Grot@h%C
      \Grot@Px\Grot@right  \Grot@r\Grot@h%B
      \Grot@Px\Grot@left   \Grot@l\Grot@d%D
      \Grot@Py\Grot@depth  \Grot@r\Grot@d%E
    \fi
  \fi
  \advance\Grot@height\Grot@y
  \advance\Grot@depth\Grot@y
  \Grot@Px\dimen@  \Grot@x\Grot@y
  \Grot@Py\dimen@ii \Grot@x\Grot@y
  \dimen@-\dimen@     \advance\dimen@-\Grot@left
  \dimen@ii-\dimen@ii \advance\dimen@ii\Grot@y
%   \GDebug{Rotate: (l,r,h,d)^^J%
% Original \the\Grot@l,\the\Grot@r,\the\Grot@h,\the\Grot@d,^^J%
% New..... \the\Grot@left,\the\Grot@right,%
%          \the\Grot@height,\the\Grot@depth}%
  \setbox\z@\hbox{%
    \kern\dimen@
    \raise\dimen@ii\hbox{%
\ifnum\textdirection=\z@\else
\typeout{[\the\Grot@left][\the\Grot@right][\the\dimen@]}%
\kern\dimexpr\Grot@right-\dimen@\relax
\raise-\dimen@ii\hbox
\fi
{\Grot@start\box\z@\Grot@end}}}%
  \ht\z@\Grot@height
  \dp\z@-\Grot@depth
  \advance\Grot@right-\Grot@left\wd\z@\Grot@right
  \leavevmode\box\z@
  \endgroup}

\makeatother
\begin{document}

a\fbox{abc}\fbox{\rotatebox[origin=Br]{90}{abcrr}}\fbox{\rotatebox[origin=Bl]{90}{abcll}}

\ifx\textdir\undefined\else
\textdir TRT
\fi

a\fbox{abc}\fbox{\rotatebox[origin=Br]{90}{abcrrr}}\fbox{\rotatebox[origin=Bl]{90}{abclll}}

\end{document}
davidcarlisle commented 4 years ago

yes I hadn't really checked the interpretation of all the origin options, just forced that example to work to see if that is the expected answer rather than rotating from the left edge.

car222222 commented 4 years ago

Another thing to resolve: since TRT is a reflection of TLT, should the meaning of a ‘positive rotation value’ also get reflected to mean ‘clockwise’ (assuming Hebrew clocks look like (most) English clocks).

It’s all relative:-).

car222222 commented 4 years ago

Why not rotate about the centre of the box? I recall ‘discussing’ this with Sebastian, who maintained volubly that it was ‘completely obvious’ what a rotation is, so no need to specify the centre!

davidcarlisle commented 4 years ago

should the meaning of a ‘positive rotation value’ also get reflected to mean ‘clockwise’

Unless there is strong push from native speakers that that is the convention I think it would be pretty confusing to change the direction.

Why not rotate about the centre of the box?

I don't think we can change the behaviour of LTR documents. The centre is the default value of the origin key so \rotatebox[]{90}{1234} rotates around the centre, as does \rotatebox[origin=c]{90}{1234}

davidcarlisle commented 4 years ago

We also need to coordinate with the patches done for xetex by the bidi package.

image

\documentclass{article}

\usepackage{graphicx,bidi}
\TeXXeTstate=1

\begin{document}

a\fbox{\rotatebox[origin=Br]{90}{abc}}

1\fbox{\rotatebox{45}{abc}}
2\fbox{\rotatebox{90}{abc}}
3\fbox{\rotatebox{-45}{abc}}
4\fbox{\rotatebox{-90}{abc}}

\setRTL

a\fbox{\rotatebox[origin=Br]{90}{abc}}

1\fbox{\rotatebox{45}{abc}}
2\fbox{\rotatebox{90}{abc}}
3\fbox{\rotatebox{-45}{abc}}
4\fbox{\rotatebox{-90}{abc}}

\end{document}
vinoruto commented 4 years ago

@davidcarlisle

Unless there is strong push from native speakers that that is the convention I think it would be pretty confusing to change the direction.

If it is of any help, I am a native speaker of Arabic (A Right to left language), however, I do not have much experience regarding the programming aspect you are discussing, I am not used to modifying packages.

car222222 commented 4 years ago

A use-case for rotating text (the only generic one I can immediately think of):

Typesetting column headings in a table, if they are long one often rotates them through 90deg (or 45deg for easier reading). Now think about an RTL document. My intuition is that the “equivalent layout” of a table in RTL is obtained by a reflection. For such headings is thus obtained by rotating the text through the opposite angle.

car222222 commented 4 years ago

I think that considering the centre of rotation is not helpful in practical use-cases. It is better to think as follows: first rotate the box (about any centre); then decide where on the page you want the reference of the “new box” to be.
Note: “new box” needs interpretation, it could be the rotated box itself, or the bounding box of the rotated box.

car222222 commented 4 years ago

@vinoruto Did you follow my use-case regarding rotated text in column-headings? Your input on this would be useful.

@davidcarlisle or @u-fischer Any chance of producing the two possibilities for such a table? Preferably using 45deg rotation as this makes the distinction very clear.

This is not entirely a question of programming although I can see arguments (at least from non-tech types) for not changing the meaning of a “positive rotation”. But therefore there is probably a need to be able to specify (at document level) the direction of a rotation that is relative to the current text direction, rather than absolutely (relatively to the page co-ordinate system].

vinoruto commented 4 years ago

@car222222

Did you follow my use-case regarding rotated text in column-headings? Your input on this would be useful

It is more appropriate for me if there is a worked example, I can provide sample text if you need so.

car222222 commented 4 years ago

I asked if @davidcarlisle or @u-fischer can make up, or find, examples of my use-case.
But they are very busy people. Maybe I shall loook for something I can adapt.

wspr commented 4 years ago

Surely @khaledhosny can provide some authoritative opinion on the rotation convention?

vinoruto commented 4 years ago

@car222222 , if it is not of any annoyance to you, can you give me a little bit more detail of what you would like to do since this issue caused me to suspend an ongoing typsetting process of a certain publication of mine. A simplified know how or an example which I can follow to generate your query in Arabic (RTL language) since I am not experienced at Latex Hacking.

davidcarlisle commented 4 years ago

@car222222 I think you mean an example like this? (made with xetex and bidi package

image

\documentclass{article}
\usepackage{graphicx}
\usepackage{bidi}

\begin{document}

\begin{tabular}{ll}
\rotatebox{45}{head1}&
\rotatebox{45}{head2}\\
\hline
  aaa&bbb
\end{tabular}

\setRTL

\begin{tabular}{ll}
\rotatebox{45}{head1}&
\rotatebox{45}{head2}\\
\hline
  aaa&bbb
\end{tabular}

\begin{tabular}{ll}
\rotatebox{-45}{head1}&
\rotatebox{-45}{head2}\\
\hline
  aaa&bbb
\end{tabular}

\end{document}
u-fischer commented 4 years ago

@vinoruto the main question is what you would expect that a \rotatebox{45}{...} does in a RTL context. Here a few possibilities:

image

davidcarlisle commented 4 years ago

I would argue against changing the direction of rotation.

CSS for example which has much more embedded support for bidirectional text than we do always takes positive rotation to be in the same direction (which it takes as clockwise actually, but it's not dependent on the text direction)

https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/rotate

It would also be confusing in diagrams and other vector drawing contexts if the rotation direction depended on the text direction of the outer context.

u-fischer commented 4 years ago

I would argue against changing the direction of rotation.

That was my first reaction too, I'm rather confident that 45 rotates everywhere in the same direction (anti-clockwise) .

wspr commented 4 years ago

Makes sense to me too. So do we think Ulrike's third example above is probably the most sensible outcome?

car222222 commented 4 years ago

Thanks for the examples, David. Yes, I think they shows the correct things but using aaa and bbb does not help thinking about writing directions:-). My claim is that the lower one in the RTL column is the "visual equivalent" of the LTR one, for the angle of the text goes. BUT the text inside both the RTL boxes is not set in RTL is it? (Impossible to tell whether the aaa and bbb have changed diretcion!)

My suggestion is to provide an extra command that rotates in the "expcted direction" relative to the writing direction.

khaledhosny commented 4 years ago

If rotation is specified using angles, then it should be direction-independent; rotating 45 degrees should do the same thing regardless of the text directions (clockwise is the same if you are speaking Arabic or Greek, Arabic clocks don’t rotate in a different direction).

It is also desirable to have direction-neutral layouts that produce the right output for a given document or text direction, but these should then use relating not absolute terms. A good example is CSS text-align: left that always aligns to the left, and text-align: start that aligns to the left or to the right depending on the direction.

u-fischer commented 4 years ago

Makes sense to me too. So do we think Ulrike's third example above is probably the most sensible outcome?

Well it depends on if you think about the mathematics or typesetting. For e.g. table headers you don't want to rotate by 45 degrees around the left corner but probably "go up by an angle of 45 in writing direction". Which for LTR means \rotatebox{45} but for RTL \rotatebox[origin=Br]{-45}. And the question is if one needs a syntax for this.

\documentclass{article}
\usepackage{graphicx}
\usepackage{bidi}

\begin{document}

\begin{tabular}{ll}
x\rotatebox{60}{xxxx}x&
x\rotatebox{60}{xxxx}x\\
\hline
  aaa&bbb
\end{tabular}

\setRTL

\begin{tabular}{ll}
x\rotatebox[origin=Br]{-60}{xxxx}x&
x\rotatebox[origin=Br]{-60}{xxxx}x\\
\hline
  aaa&bbb
\end{tabular}

\end{document}

image

car222222 commented 4 years ago

Yes, Ulrike is saying much the same as I meant.

In practice, at least for this use-case, after the rotation you will always need to translate the box to get it to 'look right', so the exact location of the rotation centre is not vital.

car222222 commented 4 years ago

But please use unsymmetric letters and words so that we can see all the details of what is really going on!

u-fischer commented 4 years ago

But please use unsymmetric letters and words so that we can see all the details of what is really going on!

I used symmmetric letters and words on purpose to avoid confusing due to missing direction changes inside the box (and tabular cells when using lualatex).

vinoruto commented 4 years ago

@u-fischer - @car222222 , I modified the code to use lualatex + Babel, and used simple obvious arabic words, I tested it out and all is working fine, If you may check it out

\documentclass{article}
\usepackage{graphicx}
\usepackage[bidi=basic,layout=lists.tabular]{babel}
\babelprovide[import=en,maparabic,main]{english}
\babelprovide[import=ar,language=Default]{arabic}
\babelfont{rm}{Latin Modern Roman}
\babelfont[arabic]{rm}{Simplified Arabic} % Can use "Amiri" instead of simplified arabic
\babelfont[english]{rm}{Times New Roman}

\begin{document}
\begin{tabular}{ll}
x\rotatebox{60}{\foreignlanguage{arabic}{ذهب}}x&
x\rotatebox{60}{\foreignlanguage{arabic}{فهم}}x\\
\hline
  \foreignlanguage{arabic}{نام}
  &
   \foreignlanguage{arabic}{ريم}\\
\hline   
   Rome & Dome
\end{tabular}

\begin{tabular}{ll}
x\rotatebox[origin=Br]{-60}{\foreignlanguage{arabic}{ذهب}}x&
x\rotatebox[origin=Br]{-60}{\foreignlanguage{arabic}{فهم}}x\\
\hline
    \foreignlanguage{arabic}{نام}
  &
   \foreignlanguage{arabic}{ريم}\\
\hline   
   Rome & Dome
\end{tabular}
\end{document}

trr - Copy (2)

vinoruto commented 4 years ago

Hey dudes, isn't there anyway to resolve this issue, it causing me some truly annoying technical difficulties.

u-fischer commented 4 years ago

I made a testfile for lualatex and checked the rotation for the various origins and a few angles. Imho the rotation and the bounding box is always fine. The only thing wrong is the vertical shift: for 0-180 is should go up, for 180-360 down by an unknown amount.

\documentclass{article}
\usepackage{graphicx}

\usepackage{tikz}
\newcommand\baseline[2][red]{\tikz[overlay]\draw[#1](0,0)--++(#2,0);\ignorespaces}
\newcommand\testtext[1]{\fboxsep=0pt \fbox{#1yytt\rule[-10pt]{1pt}{20pt}}}
\newcommand\testangle{90}
\newcommand\testrotate[1]{%
  \fbox{\rotatebox[origin=#1]{\testangle}
        {\baseline[green]{\ifnum\textdirection=0 1\else -1 \fi}\testtext{#1}}}}
\newcommand\testsuite[1]{%
     \renewcommand\testangle{#1}
     \ifnum\textdirection=0
     \baseline{20}%
     \else
     \baseline{-20}%
     \fi 
     a\fbox{\testtext{NN}}
     \testrotate{Br}
     \testrotate{Bl}
     \testrotate{lb}
     \testrotate{rb}
     \testrotate{lt}
     \testrotate{rt}
     \testrotate{c}
     }%

\begin{document}
\testsuite{90}

\testsuite{-90}

\testsuite{50}

\testsuite{-50}

\testsuite{180}

\newpage

\textdir TRT \pardir TRT

\testsuite{90}

\testsuite{-90}

\testsuite{50}

\testsuite{-50}

\testsuite{180}

\end{document}
stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity.