6

I have been trying to a achieve in ConTeXt what's called "Auto-Fit" in Adobe InDesign, specifically the "Fill Frame Proportionally" kind of Auto-Fit. And from what I gather from the description in ConTeXt Garden this should be possible. Unfortunately, however, I haven't been able to make it work, which is why I would like to ask for help.

Here's a MWE (btw, this resembles an older question discussed here):

\setuppapersize[A4][A4]
\setuplayout
[
  backspace=11.67mm,        width=131.25mm,
  topspace=21.21mm,         height=254.57mm,
  headerdistance=13pt,      header=13pt,
  footerdistance=13pt,      footer=13pt,
  rightmargindistance=13pt, rightmargin=39.5mm,
]

\setuppagenumbering[alternative=doublesided, location=]

\definehead[PageSection][section]
\setuphead[PageSection][
  page=right,
  continue=no,
  style=\ssb\bf,
]

\starttext

\chapter{Testing Proportional Fitting}

\dorecurse{3}{\input{knuth}}

\placefigure[leftpage,none]{}
  {\externalfigure[http://www.hardwickagriculture.org/blog/wp-content/uploads/placeholder.jpg] [
      width=\textwidth,
      height=\textheight,
  ]}


\PageSection{Here's a Section on a New Page}

\dorecurse{3}{\input{knuth}}

\stoptext

And this is what it looks like:

An image filling the type area in ConTeXt

As you can see, the image is scaled to the size of the type area, which is almost what I want. I would like for the image to scale proportionally, though (and be cropped as necessary). So, I did some research and found the parameter factor: max here, which seems to be perfectly suited ("aspect ratio is kept, the image is scaled to the bigger (oversized) variant"). However, the result of adding the parameter...

\placefigure[leftpage,none]{}
  {\externalfigure[http://www.hardwickagriculture.org/blog/wp-content/uploads/placeholder.jpg] [
      width=\textwidth,
      height=\textheight,
      factor=max,
  ]}

is this:

An image fitted to the type area in ConTeXt

Indeed, the image is scaling proportionally now, but it's not filling the frame anymore. So, I thought, I must have misunderstood the meaning of the factor and also tried factor: fit. Surprisingly, the result is exactly the same (i.e. no difference).

Can anybody offer any ideas as to how to achieve this? Am I on the right track trying the factor parameter? Is this a bug? Or are there other solutions?

Thanks in advance for any help.

3
  • 1
    It sounds to me like you want to do something I once wanted to do, but I'm not sure. You can look at this old thread. I hope it helps.
    – mickep
    May 18, 2017 at 11:29
  • Oh, yes. Thanks for sharing that thread. I will have to try that, but after having read through all the solutions posted, I'm pretty sure that this is more or less exactly what I was hoping for. Please post your comment as an answer, so I can accept it. By the way, actually, I assumed that factor: max was supposed to do exactly that and thought that I had found a bug. Now, judging from the last paragraph in the thread ("We should convince Hans to add this functionality (...) with factor=clip") I am wrong. Do you happen to know, is this is being worked on?
    – Marcus C.
    May 18, 2017 at 11:42
  • I am glad if it helps. I am on travel, and cannot write long answers at the moment, so I invite you to write an answer on your own with this solution, and to accept it. Good luck with the typesetting!
    – mickep
    May 18, 2017 at 11:50

3 Answers 3

6

This is not an answer but a comment. The options to \externalfigure never clip the image, so you need to use \clip as in Henri Menke's answer.

I have always struggled to understand how the keywords given to factor work. Based on the code (grph-trf.mkiv), this is a high level explanation of what is going on (in pseudocode).

If factor is set to max, fit, broad or auto then the following calculations are done (I am ignoring broad because it is somewhat complicated).

  1. First, x_size and y_size are calculated. That depends on the scale parameter, but we can approximate it as being equal to the natural width and natural height of the box. In case of images, if neither height or width are specified, x_size and y_size equal to the natural size of the image; if one is specified, say width, then x_size equals width and y_size is scaled proportionally; if both are specified, then x_size equals width and y_size equals height.

  2. Then, outer_v_size, h_size and v_size are calculated:

    if maxheight is not set
       outer_v_size = textheight
       if inner or insidefloat or inpagebody
          outer_v_size = \vsize
          scrachdimen = \vsize
       else
         if \pagegoal < \maxdimen
            if \pagetotal < \pagegoal
               scratchdimen = \pagegoal - \pagetotal
            else
               scratchdimen = outer_v_size % \textheight
            end
         else
            scratchdimen = outer_v_size % \textheight
         end
      else % maxheight is set
         scratchdimen = maxheight
         outer_v_size = scratchdimen
      end
    end  
    
    if height is empty
      v_size = scratchdimen
    else
      v_size = height
    end
    
    if width is empty 
       h_size = \hsize
    else
       h_size = width 
    end
    
  3. Then used_x_size and used_y_size are calculated.

    function calculate_norm(used, factor, maxdim, size, _size)
      switch(factor)
        case max: used = size
        case fit: used = _size
        case auto: used = maxdim
      end
    end
    
    if x_size > y_size
       caclulate_norm(used_x_size, factor, maxwidth, hsize, h_size)
       scale = used_x_size / x_size
       used_y_size = scale * y_size
    else 
       calculate_norm(used_y_size, factor, maxheight, outer_v_size, v_size)
       scale = used_y_size / y_size
       used_x_size = scale * x_size
    end
    
  4. Finally, the box or image is scaled to used_x_size and used_y_size.

Now, the key point to note is that the scaling depends on whether x_size > y_size. So, you can get the desired behaviour by setting the height and width of the image.

Here is an example to play with:

   \setupexternalfigures[location={local,default}]
\useexternalfigure
  [placeholder]
  [http://www.hardwickagriculture.org/blog/wp-content/uploads/placeholder.jpg]

\defineexternalfigure[tall][width=2\textheight, height=\textheight]
\defineexternalfigure[wide][width=\textwidth,  height=2\textwidth]

\setuppapersize[S4][S4]

\showframe
\startbuffer
  \title{None - max}
  \page
  \externalfigure[placeholder][factor=max]
  \page
  \externalfigure[mill][factor=max]

  \title{Tall - max}
  \page
  \externalfigure[placeholder][tall][factor=max]
  \page
  \externalfigure[mill][tall][factor=max]

  \title{Wide - max}
  \page
  \externalfigure[placeholder][wide][factor=max]
  \page
  \externalfigure[mill][wide][factor=max]

  \title{None - fit}
  \page
  \externalfigure[placeholder][factor=fit]
  \page
  \externalfigure[mill][factor=fit]


  \title{Tall - fit}
  \page
  \externalfigure[placeholder][tall][factor=fit]
  \page
  \externalfigure[mill][tall][factor=fit]

  \title{Wide - fit}
  \page
  \externalfigure[placeholder][wide][factor=fit]
  \page
  \externalfigure[mill][wide][factor=fit]
  \page
\stopbuffer
\starttext

\getbuffer

\setuppapersize[A4][A4]
\getbuffer
\stoptext

Note that the image is never clipped. So, if you want to clip an image, then you will need to use \clip as in Henri Menke's answer.

1
  • Thank you for the elaborate example. True, I struggled to understand what format is doing, as well, so thanks for the clarification. I will try the code as soon as possible (out of office right now), however the problem remains that I will still have to use two different definitions (i.e. the image will not automatically fill the "frame"). Actually, I thought that that's what the format parameter was supposed to achieve?
    – Marcus C.
    May 18, 2017 at 7:04
6

Use \clip. You might want to play with the hoffset parameter to get the right section of the image.

I also noticed that there is a spurious vertical space of .5\baselineskip above the picture. I couldn't find out where it comes from but I asked on the mailing list. EDIT: Herbert got the answer, add high to the location.

\starttext

\input knuth


\startplacefigure
  [location={leftpage,high,none}]

  \useexternalfigure
    [placeholder]
    [http://www.hardwickagriculture.org/blog/wp-content/uploads/placeholder.jpg]

  \clip
    [width=\textwidth,height=\textheight]
    {%
      \externalfigure
        [placeholder]
        [height=\textheight]
    }

\stopplacefigure

\stoptext

enter image description here

1
  • 1
    Thanks for the suggestion. I hadn't known \clip, so that will definitely come in handy. However, I really would like to automatically fill the "frame", no matter which proportions the original image has (because the book will be published in different formats and I don't want to adjust each of the versions manually). So, I never know beforehand, if the image is in portrait or in landscape format.
    – Marcus C.
    May 18, 2017 at 6:59
4

Following mickep's suggestion (thank you very much!) I am going to post an answer to this question myself - just in case, anybody else is having the same problem.

So, originally, I thought that some setting for the factor parameter should achieve in ConTeXt what's called "Fill Frame Proportionally" in Adobe InDesign (i.e. scale the image proportionally until it completely fills the intended space, then clip it), but that doesn't seem to be the case. Indeed, there's even a suggestion for Hans to "add this functionality to the scale macro with factor=clip" here.

Anyway, to solve my problem at hand, I copied the code suggested on this page and added it as a module (file t-scaleandclip.tex) in the right directory:

\unprotect

\newdimen\d_scaleandclip_actual_wd
\newdimen\d_scaleandclip_actual_ht

\newdimen\d_scaleandclip_requested_wd
\newdimen\d_scaleandclip_requested_ht

\newbox\scaleandclip_box

\installnamespace{scaleandclip}
\installcommandhandler \????scaleandclip {scaleandclip} \????scaleandclip

\setupscaleandclip
   [width=\textwidth,
    height=\textheight]

\unexpanded\def\scaleandclip{\dodoubleempty\doscaleandclip}

\def\doscaleandclip[#1][#2]%
   {\bgroup
    \ifsecondargument
       \edef\currentscaleandclip{#1}%
       \setupcurrentscaleandclip[#2]%
    \else\iffirstargument
       \doifassignmentelse{#1}
           {\let\currentscaleandclip\empty
            \setupcurrentscaleandclip[#1]}
           {\edef\currentscaleandclip{#1}}
     \else
       \let\currentscaleandclip\empty
     \fi\fi
     \dowithnextboxcs\scaleandclip_finish\hbox}

\def\scaleandclip_finish
     {%
       \d_scaleandclip_requested_wd \dimexpr\scaleandclipparameter\c!width\relax
       \d_scaleandclip_requested_ht \dimexpr\scaleandclipparameter\c!height\relax
       %
       \d_scaleandclip_actual_wd\wd\nextbox
       \d_scaleandclip_actual_ht\dimexpr\ht\nextbox + \dp\nextbox\relax
       %
       \ifdim\dimexpr\d_scaleandclip_actual_wd*100/\d_scaleandclip_requested_wd <
             \dimexpr\d_scaleandclip_actual_ht*100/\d_scaleandclip_requested_ht \relax
             \setbox\scaleandclip_box\hbox
                 {\scale[\c!width=\d_scaleandclip_requested_wd]{\box\nextbox}}%
             \scratchdimen=\the\dimexpr(\ht\scaleandclip_box - \d_scaleandclip_requested_ht)/2\relax
             \clip
               [
                 \c!voffset=\scratchdimen,
                 \c!height=\d_scaleandclip_requested_ht,
               ]{\box\scaleandclip_box}%
       \else
             \setbox\scaleandclip_box\hbox
                 {\scale[\c!height=\d_scaleandclip_requested_ht]{\box\nextbox}}%
             \scratchdimen=\the\dimexpr(\wd\scaleandclip_box - \d_scaleandclip_requested_wd)/2\relax
             \clip
               [
                 \c!hoffset=\scratchdimen,
                 \c!width=\d_scaleandclip_requested_wd,
               ]{\box\scaleandclip_box}%
       \fi
       \egroup}

\protect

The usage is:

\scaleandclip[width=..., height=...]{ ... any box ...}

simimlar to the usage of \scale macro.


\starttext

\dontleavehmode
\scaleandclip[width=3cm, height=3cm]{\externalfigure[cow.pdf][width=3cm,frame=on]}
\scale[maxwidth=3cm, maxheight=3cm]{\externalfigure[cow.pdf][width=3cm,frame=on]}

\dontleavehmode
\scaleandclip[width=3cm, height=3cm]{\externalfigure[cow.pdf][width=3cm,height=10cm,frame=on]}
\scale[maxwidth=3cm, maxheight=3cm]{\externalfigure[cow.pdf][width=3cm,height=10cm,frame=on]}

\dontleavehmode
\scaleandclip[width=3cm, height=3cm]{\externalfigure[cow.pdf][width=10cm,height=3cm,frame=on]}
\scale[maxwidth=3cm, maxheight=3cm]{\externalfigure[cow.pdf][width=10cm,height=3cm,frame=on]}

\stoptext

Then, after loading the module with \usemodule[scaleandclip] I can use it like this:

\placefigure[leftpage,high,none]{}{
  \scaleandclip[width=\textwidth, height=\textheight]
    {\externalfigure[http://www.hardwickagriculture.org/blog/wp-content/uploads/placeholder.jpg] [
       width=\textwidth,
       height=\textheight,
     ]}
  }

Indeed, I find that when combined with the \offset macro this even gives me an easier solution than the \bleed macro to achieve full page (bleeding) figures as discussed here.

Hope that helps. Thanks to anybody offering solutions!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .