Discussion:
[tex-k] dvips: 'fun' with translation combined with scaling/rotation
Joseph Wright
2018-03-04 15:25:55 UTC
Permalink
Hello all,

I've run into an oddity with dvips: this might well be 'user error' but
I'd appreciate any insight.

What I'm trying to do is set up box transformations inside a 'drawing'
environment. The aim is to avoid using a completely raw special (ps::
<matrix> concat) as that is opaque to the driver and means it looses
hyperlinks and the like. I can get it all working *provided* I don't
have any translation applied.

In the following example, you only get the result I was expecting if you
set \shiftmatrixtrue and \rotateandscalefalse, i.e. do everything in one
matrix operation. If you split the shift of the box out then an 'extra'
displacement pops up. It seems that the transformation (either
rotation/scaling or matrix-based) is applied to the kern which is
*before* the operations. You see the same effect if you add a y-shift
(moving the box up/down), and with a pure scaling (so no rotation at all).

The same general approach works as-expected with (x)dvipdfmx, where
again we have scaling/rotation but no driver-tracked transformation (cf.
pdfTeX), so I don't think I have the kern in the obviously-wrong place.

I suspect I'm missing a save/restore or similar: any ideas?

Joseph

\newif\ifshiftmatrix
\shiftmatrixtrue
\newif\ifrotateandscale
\rotateandscalefalse

\setbox0=\hbox{%
\special{ps::[begin]}% Line up with TeX current point
\special{ps::/ox currentpoint /oy exch def def}% Save the origin
\special{ps::@beginspecial}% Get scale and axis 'into line'
\special{ps::0 0 moveto}% (0cm,0cm)
\special{ps::85.04045 0 lineto}% (3cm,0cm)
\special{ps::28.3468 0 moveto}% (1cm,0cm}
\special{ps::28.3468 56.69363 lineto}% (1cm,2cm)
\special{ps::stroke}%
\special{ps::newpath}%
\hbox to 0pt{%
\ifshiftmatrix\else\kern 1cm \fi
\special{ps::save}%
\ifrotateandscale
% Singular value decomposition of [1 2 1 1]
\special{ps::58.28253 rotate}%
\special{ps::2.61803 -0.38197 scale}%
\special{ps::-31.71747 rotate}%
\else
\ifshiftmatrix
\special{ps::[1 2 1 1 28.3468 0] concat}%
\else
\special{ps::[1 2 1 1 0 0] concat}%
\fi
\fi
% The next three lines get us 'back' to the TeX origin
\special{ps::72 Resolution div 72 VResolution div neg scale}%
\special{ps::magscale {1 DVImag div dup scale} if}%
\special{ps::ox neg oy neg translate}%
\special{ps::[end]}%
\hbox to 0pt{Hello\hss}%
\special{ps::[begin]}%
\special{ps::restore}%
\hss
}%
\special{ps::@endspecial}% Tidy up
\special{ps::[end]}%
}%
\wd0=3cm %
\ht0=2cm %
\box0 %

world

\bye
Joseph Wright
2018-03-04 15:39:28 UTC
Permalink
Post by Joseph Wright
Hello all,
I've run into an oddity with dvips: this might well be 'user error' but
I'd appreciate any insight.
What I'm trying to do is set up box transformations inside a 'drawing'
<matrix> concat) as that is opaque to the driver and means it looses
hyperlinks and the like. I can get it all working *provided* I don't
have any translation applied.
In the following example, you only get the result I was expecting if you
set \shiftmatrixtrue and \rotateandscalefalse, i.e. do everything in one
matrix operation. If you split the shift of the box out then an 'extra'
displacement pops up. It seems that the transformation (either
rotation/scaling or matrix-based) is applied to the kern which is
*before* the operations. You see the same effect if you add a y-shift
(moving the box up/down), and with a pure scaling (so no rotation at all).
The same general approach works as-expected with (x)dvipdfmx, where
again we have scaling/rotation but no driver-tracked transformation (cf.
pdfTeX), so I don't think I have the kern in the obviously-wrong place.
I suspect I'm missing a save/restore or similar: any ideas?
Joseph
    \newif\ifshiftmatrix
    \shiftmatrixtrue
    \newif\ifrotateandscale
    \rotateandscalefalse
    \setbox0=\hbox{%
      \special{ps::[begin]}% Line up with TeX current point
      \special{ps::/ox currentpoint /oy exch def def}% Save the origin
      \special{ps::0 0 moveto}%              (0cm,0cm)
      \special{ps::85.04045 0 lineto}%       (3cm,0cm)
      \special{ps::28.3468 0 moveto}%        (1cm,0cm}
      \special{ps::28.3468 56.69363 lineto}% (1cm,2cm)
      \special{ps::stroke}%
      \special{ps::newpath}%
      \hbox to 0pt{%
        \ifshiftmatrix\else\kern 1cm \fi
        \special{ps::save}%
        \ifrotateandscale
          % Singular value decomposition of [1 2 1 1]
          \special{ps::58.28253 rotate}%
          \special{ps::2.61803 -0.38197 scale}%
          \special{ps::-31.71747 rotate}%
        \else
          \ifshiftmatrix
            \special{ps::[1 2 1 1 28.3468 0] concat}%
          \else
            \special{ps::[1 2 1 1 0 0] concat}%
          \fi
        \fi
      % The next three lines get us 'back' to the TeX origin
        \special{ps::72 Resolution div 72 VResolution div neg scale}%
        \special{ps::magscale {1 DVImag div dup scale} if}%
        \special{ps::ox neg oy neg translate}%
        \special{ps::[end]}%
        \hbox to 0pt{Hello\hss}%
        \special{ps::[begin]}%
        \special{ps::restore}%
        \hss
      }%
      \special{ps::[end]}%
    }%
    \wd0=3cm %
    \ht0=2cm %
    \box0 %
    world
    \bye
Oh, I should add that the "world" at the end deals with a second oddity:
depending on the exact set up of scaling/rotation, something goes odd
and the entire page is landscape.

Joseph
Joseph Wright
2018-03-05 07:14:26 UTC
Permalink
Post by Joseph Wright
Post by Joseph Wright
Hello all,
I've run into an oddity with dvips: this might well be 'user error'
but I'd appreciate any insight.
What I'm trying to do is set up box transformations inside a 'drawing'
<matrix> concat) as that is opaque to the driver and means it looses
hyperlinks and the like. I can get it all working *provided* I don't
have any translation applied.
In the following example, you only get the result I was expecting if
you set \shiftmatrixtrue and \rotateandscalefalse, i.e. do everything
in one matrix operation. If you split the shift of the box out then an
'extra' displacement pops up. It seems that the transformation (either
rotation/scaling or matrix-based) is applied to the kern which is
*before* the operations. You see the same effect if you add a y-shift
(moving the box up/down), and with a pure scaling (so no rotation at all).
The same general approach works as-expected with (x)dvipdfmx, where
again we have scaling/rotation but no driver-tracked transformation
(cf. pdfTeX), so I don't think I have the kern in the obviously-wrong
place.
I suspect I'm missing a save/restore or similar: any ideas?
Joseph
     \newif\ifshiftmatrix
     \shiftmatrixtrue
     \newif\ifrotateandscale
     \rotateandscalefalse
     \setbox0=\hbox{%
       \special{ps::[begin]}% Line up with TeX current point
       \special{ps::/ox currentpoint /oy exch def def}% Save the origin
       \special{ps::0 0 moveto}%              (0cm,0cm)
       \special{ps::85.04045 0 lineto}%       (3cm,0cm)
       \special{ps::28.3468 0 moveto}%        (1cm,0cm}
       \special{ps::28.3468 56.69363 lineto}% (1cm,2cm)
       \special{ps::stroke}%
       \special{ps::newpath}%
       \hbox to 0pt{%
         \ifshiftmatrix\else\kern 1cm \fi
         \special{ps::save}%
         \ifrotateandscale
           % Singular value decomposition of [1 2 1 1]
           \special{ps::58.28253 rotate}%
           \special{ps::2.61803 -0.38197 scale}%
           \special{ps::-31.71747 rotate}%
         \else
           \ifshiftmatrix
             \special{ps::[1 2 1 1 28.3468 0] concat}%
           \else
             \special{ps::[1 2 1 1 0 0] concat}%
           \fi
         \fi
       % The next three lines get us 'back' to the TeX origin
         \special{ps::72 Resolution div 72 VResolution div neg scale}%
         \special{ps::magscale {1 DVImag div dup scale} if}%
         \special{ps::ox neg oy neg translate}%
         \special{ps::[end]}%
         \hbox to 0pt{Hello\hss}%
         \special{ps::[begin]}%
         \special{ps::restore}%
         \hss
       }%
       \special{ps::[end]}%
     }%
     \wd0=3cm %
     \ht0=2cm %
     \box0 %
     world
     \bye
depending on the exact set up of scaling/rotation, something goes odd
and the entire page is landscape.
Joseph
It occurs to me that the dvipdfmx comparison is likely misleading: in
the latter, rotation and scaling is covered by driver-specific specials,
but for dvips only 'raw' PostScipt is available.

However, the core question remains: what is it that leads the
transformation to apply to kerns/box shifts *before* it is inserted?
(And indeed, what exactly determines the scope of the transformation.)
If the answer is 'it just is', I can adjust by working out the requested
shifts allowing for the subsequent transformation. But I'd like to be
sure it's entirely predictable, at least.

Joseph
Tomas Rokicki
2018-03-05 07:32:40 UTC
Permalink
I don’t think dvips supports what you are trying to do.

I’m also not sure what you are trying to accomplish. Can you clarify?

-tom

On Sun, Mar 4, 2018 at 11:14 PM Joseph Wright <
Post by Joseph Wright
Post by Joseph Wright
Post by Joseph Wright
Hello all,
I've run into an oddity with dvips: this might well be 'user error'
but I'd appreciate any insight.
What I'm trying to do is set up box transformations inside a 'drawing'
<matrix> concat) as that is opaque to the driver and means it looses
hyperlinks and the like. I can get it all working *provided* I don't
have any translation applied.
In the following example, you only get the result I was expecting if
you set \shiftmatrixtrue and \rotateandscalefalse, i.e. do everything
in one matrix operation. If you split the shift of the box out then an
'extra' displacement pops up. It seems that the transformation (either
rotation/scaling or matrix-based) is applied to the kern which is
*before* the operations. You see the same effect if you add a y-shift
(moving the box up/down), and with a pure scaling (so no rotation at all).
The same general approach works as-expected with (x)dvipdfmx, where
again we have scaling/rotation but no driver-tracked transformation
(cf. pdfTeX), so I don't think I have the kern in the obviously-wrong
place.
I suspect I'm missing a save/restore or similar: any ideas?
Joseph
\newif\ifshiftmatrix
\shiftmatrixtrue
\newif\ifrotateandscale
\rotateandscalefalse
\setbox0=\hbox{%
\special{ps::[begin]}% Line up with TeX current point
\special{ps::/ox currentpoint /oy exch def def}% Save the origin
\special{ps::0 0 moveto}% (0cm,0cm)
\special{ps::85.04045 0 lineto}% (3cm,0cm)
\special{ps::28.3468 0 moveto}% (1cm,0cm}
\special{ps::28.3468 56.69363 lineto}% (1cm,2cm)
\special{ps::stroke}%
\special{ps::newpath}%
\hbox to 0pt{%
\ifshiftmatrix\else\kern 1cm \fi
\special{ps::save}%
\ifrotateandscale
% Singular value decomposition of [1 2 1 1]
\special{ps::58.28253 rotate}%
\special{ps::2.61803 -0.38197 scale}%
\special{ps::-31.71747 rotate}%
\else
\ifshiftmatrix
\special{ps::[1 2 1 1 28.3468 0] concat}%
\else
\special{ps::[1 2 1 1 0 0] concat}%
\fi
\fi
% The next three lines get us 'back' to the TeX origin
\special{ps::72 Resolution div 72 VResolution div neg scale}%
\special{ps::magscale {1 DVImag div dup scale} if}%
\special{ps::ox neg oy neg translate}%
\special{ps::[end]}%
\hbox to 0pt{Hello\hss}%
\special{ps::[begin]}%
\special{ps::restore}%
\hss
}%
\special{ps::[end]}%
}%
\wd0=3cm %
\ht0=2cm %
\box0 %
world
\bye
depending on the exact set up of scaling/rotation, something goes odd
and the entire page is landscape.
Joseph
It occurs to me that the dvipdfmx comparison is likely misleading: in
the latter, rotation and scaling is covered by driver-specific specials,
but for dvips only 'raw' PostScipt is available.
However, the core question remains: what is it that leads the
transformation to apply to kerns/box shifts *before* it is inserted?
(And indeed, what exactly determines the scope of the transformation.)
If the answer is 'it just is', I can adjust by working out the requested
shifts allowing for the subsequent transformation. But I'd like to be
sure it's entirely predictable, at least.
Joseph
--
-- http://cube20.org/ -- http://golly.sf.net/ --
Joseph Wright
2018-03-05 09:51:17 UTC
Permalink
I don’t think dvips supports what you are trying to do.
I’m also not sure what you are trying to accomplish. Can you clarify?
-tom
Hi Tom,

I'll try to: this might be a bit long!

The background here is I'm looking at how one creates 'drawings':
inserting specials for lines, etc., into a box such that it 'works' as
part of a document. The well-known TikZ package does that, and I'll
looking at the code and trying to understand it. It's not always clear
why things are done a particular way: driver interactions are often complex.

More specifically, if one creates such a 'drawing', depending on the
driver in use you need to fiddle about with the reference point, etc.
That is the case in dvips but not in pdfTeX (direct PDF output). That
has a knock-on if you want to then put some normal TeX material inside a
'drawing' (e.g. 'Put the word XXX at the end of a line drawn from the
drawing origin to (1cm,1cm)'). All of the strange PostScript fiddling
about in the example I sent is meant to allow that. Crucially, from a
TeX point of view all of the 'drawing' stuff has no size, so any TeX
material is inserted at (or relative to) the reference point of the
\hbox everything is inside (this is the origin of the drawing).

Everything works fine for the simple case of the TeX 'inner' box being
inserted at the origin with no scaling/rotation/shear. If we want to do
those operations, we have to apply some driver-specific code or use raw
PostScript/PDF insertion, depending on the driver involved. In dvips, we
don't have high-level functions so it comes down to using a raw
PostScript concat operation. This also works fine: the text is not
displaced and does get transformed as requested.

The other thing we might make to a box is to shift it (to put the
content somewhere other than at the origin). That can be done two ways:
by doing it from the 'TeX side' using \kern, \raise, \lower, etc., or
from the 'PostScript side' by adding a shift component to the concat
operation. If you look at TikZ, with dvips it does the latter, but with
pdfTeX in PDF mode it uses \kern + \lower for the shifts, and only uses
the CTM to apply any scalings, etc. Again, this works fine: it's when I
combine the two that odd stuff happens.

Ideally, I'd like to be able to split up 'move the box around' and
'change the shape of the box'. So I was aiming to use a common flow:

- 'Escape' from the drawing set up (driver-dependent)
- Adjust the box position using a horizontal \kern and a \lower
- Change the box shape using a driver-specific application of the CTM
("\pdfsetmatrix" for pdfTeX, "pdf:btrans" for (x)dvipdfmx, "ps::
<matrix> concat" for dvips)
- Insert the box (at zero size)
- Tidy up (end the CTM scope, etc.)

Initially I thought it all worked well: doing either 'change the box
shape' or 'shift the box' is fine across all of the drivers I've tried.
The only place it's fallen down (at least I've found so far) is
combining a shift and a shape-change with dvips.

As I said at the start, my assumption is I'm missing something about the
dvips set up. If that comes down to 'You can't separate out the two
things in dvips' then I'll go back to handling all of it together. I'm
mainly wanting to make sure I've not missed something obvious.

Joseph
Akira Kakuto
2018-03-09 09:33:56 UTC
Permalink
The result is pretty bad: ...
That is the intended result.

Best,
Akira

Loading...