+(define (lookup-paper-name module name landscape?)
+ "Look up @var{name} and return a number pair of width and height,
+where @var{landscape?} specifies whether the dimensions should be swapped
+unless explicitly overriden in the name."
+ (let* ((swapped?
+ (cond ((string-suffix? "landscape" name)
+ (set! name
+ (string-trim-right (string-drop-right name 9)))
+ #t)
+ ((string-suffix? "portrait" name)
+ (set! name
+ (string-trim-right (string-drop-right name 8)))
+ #f)
+ (else landscape?)))
+ (is-paper? (module-defined? module 'is-paper))
+ (entry (and is-paper?
+ (eval-carefully (assoc-get name paper-alist)
+ module
+ #f))))
+ (and entry is-paper?
+ (if swapped? (cons (cdr entry) (car entry)) entry))))
+
+(define (set-paper-dimensions m w h landscape?)
+ "M is a module (i.e. layout->scope_ )"
+ (let*
+ ;; page layout - what to do with (printer specific!) margin settings?
+ ((paper-default (or (lookup-paper-name
+ m (ly:get-option 'paper-size) landscape?)
+ (cons w h)))
+ ;; Horizontal margins, marked with #t in the cddr, are stored
+ ;; in renamed variables because they must not be overwritten.
+ ;; The cadr indicates whether a value is a vertical dimension.
+ ;; Output_def::normalize () needs to know
+ ;; whether the user set the value or not.
+ (scaleable-values '(("left-margin" #f . #t)
+ ("right-margin" #f . #t)
+ ("inner-margin" #f . #t)
+ ("outer-margin" #f . #t)
+ ("binding-offset" #f . #f)
+ ("top-margin" #t . #f)
+ ("bottom-margin" #t . #f)
+ ("indent" #f . #f)
+ ("short-indent" #f . #f)))
+ (scaled-values
+ (map
+ (lambda (entry)
+ (let ((entry-symbol
+ (string->symbol
+ (string-append (car entry) "-default")))
+ (vertical? (cadr entry)))
+ (cons (if (cddr entry)
+ (string-append (car entry) "-default-scaled")
+ (car entry))
+ (round (* (if vertical? h w)
+ (/ (eval-carefully entry-symbol m 0)
+ ((if vertical? cdr car)
+ paper-default)))))))
+ scaleable-values)))
+
+ (module-define! m 'paper-width w)
+ (module-define! m 'paper-height h)
+ ;; Sometimes, lilypond-book doesn't estimate a correct line-width.
+ ;; Therefore, we need to unset line-width.
+ (module-remove! m 'line-width)
+
+ (for-each
+ (lambda (value)
+ (let ((value-symbol (string->symbol (car value)))
+ (number (cdr value)))
+ (module-define! m value-symbol number)))
+ scaled-values)))
+
+(define (internal-set-paper-size module name landscape?)
+ (let* ((entry (lookup-paper-name module name landscape?))
+ (is-paper? (module-defined? module 'is-paper)))
+ (cond
+ ((not is-paper?)
+ (ly:warning (_ "This is not a \\layout {} object, ~S") module))
+ (entry
+ (set-paper-dimensions module (car entry) (cdr entry) landscape?)
+
+ (module-define! module 'papersizename name)
+ (module-define! module 'landscape
+ (if landscape? #t #f)))
+ (else
+ (ly:warning (_ "Unknown paper size: ~a") name)))))
+
+(define-safe-public (set-default-paper-size name . rest)
+ (let* ((pap (module-ref (current-module) '$defaultpaper))
+ (new-paper (ly:output-def-clone pap))
+ (new-scope (ly:output-def-scope new-paper)))
+ (internal-set-paper-size
+ new-scope
+ name
+ (memq 'landscape rest))
+ (module-set! (current-module) '$defaultpaper new-paper)))
+
+(define-public (set-paper-size name . rest)
+ (if (module-defined? (current-module) 'is-paper)
+ (internal-set-paper-size (current-module) name
+ (memq 'landscape rest))
+
+ ;;; TODO: should raise (generic) exception with throw, and catch
+ ;;; that in parse-scm.cc
+ (ly:warning (_ "Must use #(set-paper-size .. ) within \\paper { ... }"))))
+
+(define-public (scale-layout paper scale)
+ "Return a clone of the paper, scaled by the given scale factor."
+ (let* ((new-paper (ly:output-def-clone paper))
+ (dim-vars (ly:output-def-lookup paper 'dimension-variables))
+ (old-scope (ly:output-def-scope paper))
+ (scope (ly:output-def-scope new-paper)))
+
+ (for-each
+ (lambda (v)
+ (let* ((var (module-variable old-scope v))
+ (val (if (variable? var) (variable-ref var) #f)))