Subject: 29.0.50; preview-get-dpi produces surprising result on vertical monitor

From: Tony Zorman <[email-masked]>
Date: Sun, 06 Nov 2022 16:31:22 +0100

Hi,

tl;dr: the `preview-get-dpi' function compares the wrong values for
horizontal monitors (i.e., ones that are flipped by 90/270 degrees).

When previewing LaTeX fragments via AUCTeX's `preview.el' library,
things get quite messed up when one or more monitors are set up in
"portrait mode".  I have attached three images that show this; bug1.png
shows a rendered equation for two vertical monitors, bug2.png for a
single one, and horiz.png displays that same equation being rendered on
a horizontal monitor (i.e., this is the expected result).

Looking into `preview.el', it seems like that `preview-get-dpi' (called
by `preview-get-geometry', which is in turn called by
`preview-generate-preview') calculates the wrong "resolution".  The
relevant implementation is

    (defun preview-get-dpi ()
      (let* ((monitor-attrs (frame-monitor-attributes))
             (mm-dims (cdr (assoc 'mm-size monitor-attrs)))
             (mm-width (nth 0 mm-dims))
             (mm-height (nth 1 mm-dims))
             (pixel-dims (cl-cdddr (assoc 'geometry monitor-attrs)))
             (pixel-width (nth 0 pixel-dims))
             (pixel-height (nth 1 pixel-dims)))
        (cons (/ (* 25.4 pixel-width) mm-width)
              (/ (* 25.4 pixel-height) mm-height))))

An example output of `frame-monitor-attributes' for a horizontal
monitor:

    '((name . "DP1")
      (geometry 0 0 1920 1080)
      (workarea 0 0 1920 1080)
      (mm-size 530 300)
      (frames <<omitted>>)
      (source . "XRandR 1.5"))

The same monitor in "vertical-mode" returns:

    '((name . "DP1")
      (geometry 0 0 1080 1920)
      (workarea 0 0 1080 1920)
      (mm-size 530 300)
      (frames <<omitted>>)
      (source . "XRandR 1.5"))

Crucially, the returned physical width and height of the monitor don't
change, but the geometry—what will be the pixel width and height—does.
However, turning the monitor by 90 degrees obviously switches the
physical width an height around as well, although that perhaps can't be
reported property.  In such a situation, we actually compare the pixel
_width_ of the monitor with its physical _height_, as well as its pixel
height with its width.  Naturally, and depending on the specific setup,
this produces too narrow or too wide previews.

I have fixed this locally by only comparing comparable values; i.e.,
instead of

    (cons (/ (* 25.4 pixel-width) mm-width)
          (/ (* 25.4 pixel-height) mm-height))

I wrote something like

    (cons (/ (* 25.4 (max pixel-width pixel-height)) (max mm-width mm-height))
          (/ (* 25.4 (min pixel-width pixel-height)) (min mm-width mm-height)))

This fixed the problem for me, but I don't know if there is some
situation (or strange resolution) in which it would be correct to
compare some other combination of values.

Hopefully this was comprehensible.  If not, feel free to ask for more
information.

Best,
  Tony

In GNU Emacs 29.0.50 (build 1, x86_64-pc-linux-musl, cairo version
 1.16.0) of 2022-10-29 built on pbox
Repository revision: 3b0296c8b03436c7848009ca0238b1126dc3bac7
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12101004
System Description: Void Linux

Configured using:
 'configure --with-x --without-x-toolkit --without-toolkit-scroll-bars
 --without-dbus --without-gconf --without-gsettings --with-modules
 --with-file-notification=inotify --with-jpeg --with-tiff --with-gif
 --with-png --with-xpm --with-rsvg --without-imagemagick --with-cairo
 --with-gnutls --with-sound --with-json --with-harfbuzz --with-gpm
 --with-native-compilation --without-compress-install --with-xinput2
 --with-small-ja-dic 'CFLAGS=-O2 -pipe -march=native -mtune=native
 -fomit-frame-pointer''

Configured features:
CAIRO FREETYPE GIF GLIB GMP GNUTLS HARFBUZZ JPEG JSON LCMS2 LIBOTF
LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY INOTIFY OLDXMENU PDUMPER PNG
RSVG SECCOMP SOUND SQLITE3 THREADS TIFF WEBP X11 XDBE XIM XINPUT2 XPM
ZLIB

Important settings:
  value of $LC_ALL: en_US.UTF-8
  value of $LC_COLLATE: C
  value of $LANG: en_GB
  locale-coding-system: utf-8-unix

Major mode: Message

Minor modes in effect:
  olivetti-mode: t
  rainbow-mode: t
  rainbow-delimiters-mode: t
  flyspell-mode: t
  mml-mode: t
  corfu-indexed-mode: t
  corfu-history-mode: t
  global-corfu-mode: t
  corfu-mode: t
  savehist-mode: t
  electric-pair-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  xclip-mode: t
  global-auto-revert-mode: t
  global-hl-line-mode: t
  repeat-mode: t
  recentf-mode: t
  save-place-mode: t
  pixel-scroll-precision-mode: t
  global-subword-mode: t
  subword-mode: t
  popwin-mode: t
  marginalia-mode: t
  vertico-mode: t
  whole-line-or-region-global-mode: t
  whole-line-or-region-local-mode: t
  gcmh-mode: t
  override-global-mode: t
  ctrlf-mode: t
  ctrlf-local-mode: t
  puni-mode: t
  auto-compile-on-load-mode: t
  auto-compile-on-save-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  column-number-mode: t
  line-number-mode: t
  auto-fill-function: yas--auto-fill
  visual-line-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  abbrev-mode: t

Load-path shadows:
/usr/share/emacs/site-lisp/notmuch/notmuch hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch
/usr/share/emacs/site-lisp/notmuch/notmuch-wash hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-wash
/usr/share/emacs/site-lisp/notmuch/notmuch-tree hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-tree
/usr/share/emacs/site-lisp/notmuch/notmuch-tag hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-tag
/usr/share/emacs/site-lisp/notmuch/notmuch-show hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-show
/usr/share/emacs/site-lisp/notmuch/notmuch-query hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-query
/usr/share/emacs/site-lisp/notmuch/notmuch-print hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-print
/usr/share/emacs/site-lisp/notmuch/notmuch-parser hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-parser
/usr/share/emacs/site-lisp/notmuch/notmuch-mua hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-mua
/usr/share/emacs/site-lisp/notmuch/notmuch-message hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-message
/usr/share/emacs/site-lisp/notmuch/notmuch-maildir-fcc hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-maildir-fcc
/usr/share/emacs/site-lisp/notmuch/notmuch-lib hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-lib
/usr/share/emacs/site-lisp/notmuch/notmuch-jump hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-jump
/usr/share/emacs/site-lisp/notmuch/notmuch-hello hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-hello
/usr/share/emacs/site-lisp/notmuch/notmuch-draft hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-draft
/usr/share/emacs/site-lisp/notmuch/notmuch-crypto hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-crypto
/usr/share/emacs/site-lisp/notmuch/notmuch-compat hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-compat
/usr/share/emacs/site-lisp/notmuch/notmuch-company hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-company
/usr/share/emacs/site-lisp/notmuch/notmuch-address hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/notmuch-address
/usr/share/emacs/site-lisp/notmuch/coolj hides /home/slot/.config/emacs/elpa/notmuch-20220923.2323/coolj
/home/slot/.config/emacs/elpa/transient-20221028.1430/transient hides /usr/local/share/emacs/29.0.50/lisp/transient
/home/slot/.config/emacs/lisp/completion hides /usr/local/share/emacs/29.0.50/lisp/completion
/home/slot/.config/emacs/elpa/modus-themes-20221101.424/theme-loaddefs hides /usr/local/share/emacs/29.0.50/lisp/theme-loaddefs

Features:
(gnus-alias org-notmuch the-org-mode-expansions org-element avl-tree
generator org-protocol org ob ob-tangle ob-ref ob-lob ob-table ob-exp
org-macro org-footnote org-src ob-comint org-pcomplete pcomplete
org-list org-faces org-entities noutline outline org-version
ob-emacs-lisp ob-core ob-eval org-table oc-basic bibtex iso8601 ol
org-keys oc org-compat org-macs org-loaddefs mailboxes notmuch
notmuch-tree notmuch-jump notmuch-hello notmuch-show notmuch-print
notmuch-crypto notmuch-mua notmuch-message notmuch-draft
notmuch-maildir-fcc notmuch-address notmuch-company notmuch-parser
format-spec notmuch-wash diff-mode coolj notmuch-query goto-addr
icalendar diary-lib diary-loaddefs cal-menu calendar cal-loaddefs
notmuch-tag crm notmuch-lib notmuch-version notmuch-compat mm-view
mml-smime smime gnutls dig embark-consult embark ffap wgrep grep compile
comint ansi-osc ansi-color consult-vertico consult compat-28 bookmark
rect face-remap olivetti expand-region subword-mode-expansions
text-mode-expansions er-basic-expansions thingatpt expand-region-core
expand-region-custom puni pulse mwim eudc-capf eudc cus-start eudc-vars
jka-compr helpful cc-langs cc-vars cc-defs imenu trace edebug debug
backtrace info-look find-func f f-shortdoc shortdoc help-fns radix-tree
elisp-refs shadow sort rainbow-mode xterm-color color rainbow-delimiters
flyspell ispell mail-extr emacsbug message yank-media puny dired-x dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util text-property-search time-date mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047
rfc2045 ietf-drums mm-util mail-prsvr mail-utils mule-util corfu-indexed
corfu-history corfu orderless cursor-sensor savehist org-setup
latex-config latex-pretty-symbols latex-math eshell-config programming
agda2 erc-config rss email writing parens-config elec-pair
global-packages undo-tree diff queue yasnippet xclip autorevert
filenotify hl-line repeat recentf tree-widget saveplace pixel-scroll
cua-base ring cap-words superword subword dired-config window-management
popwin completion marginalia vertico keybindings whole-line-or-region
edmacro kmacro theming modus-operandi-theme modus-themes cus-edit pp
cus-load wid-edit pcase smart-mode-line-respectful-theme smart-mode-line
advice rich-minority startup-screen sensible-defaults finder-inf s dash
gcmh quelpa-use-package use-package use-package-ensure
use-package-delight use-package-diminish use-package-bind-key bind-key
use-package-core aas-autoloads arxiv-citation-autoloads
auto-compile-autoloads avy-autoloads cdlatex-autoloads cider-autoloads
clojure-mode-autoloads consult-notmuch-autoloads corfu-autoloads
csv-mode-autoloads ctrlf-autoloads dictcc-autoloads diff-hl-autoloads
dired-narrow-autoloads dired-hacks-utils-autoloads diredfl-autoloads
ef-themes-autoloads elfeed-score-autoloads elfeed-autoloads
emacs-anywhere-autoloads embark-consult-autoloads consult-autoloads
embark-autoloads ement-autoloads erc-hl-nicks-autoloads
esh-autosuggest-autoloads company-autoloads eshell-toggle-autoloads
exercism-autoloads async-await-autoloads async-autoloads a-autoloads
expand-region-autoloads flycheck-autoloads forge-autoloads
closql-autoloads gcmh-autoloads ghub-autoloads gnus-alias-autoloads
haskell-mode-autoloads helpful-autoloads elisp-refs-autoloads
hide-mode-line-autoloads hindent-autoloads hl-todo-autoloads
iter2-autoloads ivy-autoloads ix.io-autoloads kbd-mode-autoloads
keycast-autoloads khalel-autoloads latex-change-env-autoloads
ledger-mode-autoloads lsp-haskell-autoloads lsp-ltex-autoloads
lsp-ui-autoloads lsp-mode-autoloads ht-autoloads lv-autoloads
magit-delta-autoloads magit-autoloads git-commit-autoloads
marginalia-autoloads math-delimiters-autoloads modus-themes-autoloads
multiple-cursors-autoloads mwim-autoloads nano-theme-autoloads
nix-mode-autoloads notmuch-autoloads olivetti-autoloads
orderless-autoloads org-appear-autoloads org-auctex-autoloads
auctex-autoloads tex-site org-present-autoloads org-roam-ui-autoloads
org-roam-autoloads emacsql-sqlite-autoloads emacsql-autoloads
packed-autoloads paredit-autoloads parseedn-autoloads parseclj-autoloads
persist-autoloads pkg-info-autoloads epl-autoloads plz-autoloads
popup-autoloads popwin-autoloads pos-tip-autoloads promise-autoloads
puni-autoloads easy-mmode quelpa-use-package-autoloads quelpa-autoloads
rainbow-delimiters-autoloads rainbow-mode-autoloads request-autoloads
rustic-autoloads markdown-mode-autoloads f-autoloads rust-mode-autoloads
s-autoloads sesman-autoloads showtip-autoloads simple-httpd-autoloads
smart-mode-line-autoloads rich-minority-autoloads spinner-autoloads
stimmung-themes-autoloads svg-lib-autoloads taxy-magit-section-autoloads
taxy-autoloads magit-section-autoloads dash-autoloads
transient-autoloads treepy-autoloads undo-tree-autoloads queue-autoloads
use-package-autoloads bind-key-autoloads vertico-autoloads
visual-fill-column-autoloads websocket-autoloads wgrep-autoloads
whole-line-or-region-autoloads with-editor-autoloads info
compat-autoloads xclip-autoloads xterm-color-autoloads yaml-autoloads
yasnippet-autoloads auto-compile comp comp-cstr warnings icons rx
cl-extra help-mode packed compat compat-macs package browse-url url
url-proxy url-privacy url-expand url-methods url-history url-cookie
generate-lisp-file url-domsuf url-util mailcap url-handlers url-parse
auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x
map byte-opt gv bytecomp byte-compile url-vars cl-loaddefs cl-lib rmc
iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode lisp-mode prog-mode register
page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads inotify lcms2 dynamic-setting font-render-setting cairo xinput2
x multi-tty make-network-process native-compile emacs)

Memory information:
((conses 16 595812 522491)
 (symbols 48 82187 28)
 (strings 32 167057 27313)
 (string-bytes 1 5017839)
 (vectors 16 50353)
 (vector-slots 8 1125698 317543)
 (floats 8 434 762)
 (intervals 56 3989 5338)
 (buffers 984 15))

-- 
Tony Zorman | https://tony-zorman.com/