动态图片大小

动态图片大小

考虑以下 MWE:

\documentclass{article}
\usepackage{graphicx}
\usepackage{lipsum}
\usepackage{float}
\usepackage{duckuments}

\begin{document}
    \lipsum[1-4]

    \begin{figure}[H]
    \centering
    \includegraphics{example-image-duck}
    \caption{This is a duck}
    \end{figure}

    \lipsum[4-7]
\end{document}

正如您所看到的,在编译 MWE 后,它将生成 2 页,将图像移动到第二页,因为它在第一页上已经放不下了。而且因为我使用了H浮动环境选项,所以第一页上现在有一个很大的空白区域。我知道我可以通过让图像浮动来避免这种情况,但这不是本文的目的。

如果我在某个文档中遇到这种情况,我会继续调整图像的大小,直到它最终适合整个页面。通过这样做,我会手动尝试不同的尺寸(在这种情况下,让它越来越小,直到适合)。如果我不得不将图像缩小太多,我会让它保持原样,接受第一页上的空白空间。

我想要的是一种可以对每幅图像自动执行此过程的方法:

  1. 检查浮动环境是否适合当前页面(图像和标题是否有足够的空间)。如果是,则将其放在那里并退出。
  2. 如果剩余空间允许图像以原始高度的 x%(例如 50%)显示,则将其缩小并插入此处。
  3. 如果空间不足,请将图片放在下一页的顶部

此外,如果必须使用类似浮动的行为,那就太好了3.:将图像放在下一页的顶部,并允许在源中指定的文本要插入的图像直到空白处被填满。

我心里还想着其他一些事情,它们会进一步完善系统,但这些是“核心功能”。

然而,在我开始尝试自己实现此功能之前,我想问一下是否已经有可以执行所述操作或执行类似操作的软件包可用于构建所需的功能。对于如何总体上完成整个过程的任何想法也欢迎提出。

如果没有可用的解决方案,以下是我对自定义实现的想法:

获取剩余高度应该相当简单获取浮动的高度也应该可行。因此,问题最终归结为如何在浮动环境中更改包含的图像的大小。一种简单的方法是解析浮点数直到\includegraphics遇到,并添加必要的可选参数。如果已经有可选参数,它们应该相应地改变(添加一个因子)。然而,这是我不知道该怎么做的部分。

答案1

可以使用tikzpagenodes\pgfextracty来确定页面上的剩余空间。请注意,图中增加了\intextsep上方的间隙(下方的间隙被页面底部吸收)。

\documentclass{article}
\usepackage{graphicx}
\usepackage{lipsum}
\usepackage{float}
\usepackage{duckuments}
\usepackage{showframe}

\usepackage{tikzpagenodes}
\newlength{\checkspaceleft}% reserve global name

\newcommand{\checkspace}{\ifvmode\else\par\fi
  \noindent\tikz[remember picture,overlay]{%
    \pgfextracty{\checkspaceleft}{\pgfpointdiff%
      {\pgfpointanchor{current page text area}{south}}%
      {\pgfpointorigin}}%
    \global\checkspaceleft=\checkspaceleft}%
  \hrule height0pt}% not on same line

\newcommand{\myfigure}[2]{% #1 = image, #2 = caption
  \checkspace
  \begin{figure}[H]
    \sbox0{\begin{minipage}[b]{\textwidth}
      #2
      \end{minipage}}% measure caption
    \dimen0=\dimexpr \checkspaceleft-\ht0-\dp0-\intextsep\relax
    \sbox1{#1}% measure image
    \centering
    \ifdim \dimen0<\ht1\relax
      \resizebox{!}{\dimen0}{\usebox1}
    \else
      \usebox1
    \fi
    \usebox0
  \end{figure}}

\begin{document}
    \lipsum[1-4]

    \myfigure{\includegraphics{example-image-duck}}%
      {\caption{This is a duck}}

    \lipsum[4-7]
\end{document}

答案2

好吧,我继续自己构建一些东西。我决定创建一个包,所以当您想要复制和粘贴我的代码时,您必须dynimage.sty在工作目录中创建一个文件并将以下代码粘贴到其中:

\NeedsTeXFormat{LaTeX2e}

\ProvidesPackage{dynimage}[2018/09/14 Package for dynamic images that will fit on current page]

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% REQUIREMENTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\RequirePackage{pgfkeys}
\RequirePackage{etoolbox}
\RequirePackage{xstring}
\RequirePackage{graphicx}
\RequirePackage{pgf}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% INTERNAL VARS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newtoggle{@dynimageDebug}
\def\@dyinmageMinScaleFactor{0.75}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% KEY SETUP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\pgfkeys{%
    /dynimage/.cd,%
    min scale factor/.estore in = {\@dyinmageMinScaleFactor},%
    min scale factor/.value required,%
    reckless/.style = {/dynimage/min scale factor=0.1},%
    reckless/.value forbidden,%
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CONFIGURATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\DeclareOption{debug}{\toggletrue{@dynimageDebug}}

\DeclareOption*{%
    \pgfkeys{%
        /dynimage/.cd,%
        \CurrentOption,%
    }%
}

\ProcessOptions\relax


%%%%%%%%%%%%%%%%%%%%%%%%%%%%% IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\DeclareRobustCommand{\dynImage}[2][]{%
    \iftoggle{@dynimageDebug}{%
        \PackageInfo{dynimage}{Minimum scale factor set to \@dyinmageMinScaleFactor}
    }{}%
    %
    % Get the height of the image if it was placed without any further processing
    \newlength{\@nativeImageHeight}%
    \settoheight{\@nativeImageHeight}{\hbox{\includegraphics[#1]{#2}}}%
    %
    % Get the remaining page height
    \newlength{\@remainingPageHeight}%
    \@remainingPageHeight=\dimexpr\textheight-\pagetotal\relax%
    %
    % Calculate the scaling-factor that needs to be used in order to keep the image on the current page
    \pgfmathparse{(floor(\@remainingPageHeight))/ceil(max(\@nativeImageHeight,0.1))}%
    \edef\@scaleFactor{\pgfmathresult}
    %
    % Log all gathered sizes until this point if in debug mode
    \iftoggle{@dynimageDebug}{\PackageInfo{dynimage}{The scale for “#2” is \the\@remainingPageHeight /\the\@nativeImageHeight =\@scaleFactor}}{}
    %
    % don't enlarge images (no up-scaling)
    \ifdimgreater{\@scaleFactor pt}{1pt}{\gdef\@scaleFactor{1}}{}%
    %
    % don't scale down more than allowed
    \ifdimgreater{\@scaleFactor pt}{\@dyinmageMinScaleFactor pt}{}{\gdef\@scaleFactor{1}\gdef\@tooMuchScaling{}}%
    %
    % scale if the scale isn't equal to 1
    \ifdimequal{\@scaleFactor pt}{1pt}{%
        % Output appropriate info/warning
        \ifdef{\@tooMuchScaling}{%
            \PackageWarning{dynimage}{The image "#2" would require too much down-scaling and is therefore placed on the next side}%
        }{%
            \PackageInfo{dynimage}{The image "#2" fits on the page without height-adjustments}%
        }%
        %
        % insert image as is
        \includegraphics[#1]{#2}%
    }{%
        % scale down
        %
        % calculate the scaled image height
        \pgfmathparse{\@scaleFactor * \@nativeImageHeight}
        \let\@scaledImageHeight\pgfmathresult
        %
        % Add some difference between remaining height and scaled image height in order to
        % avoid rounding inaccuracies
        \pgfmathparse{(\@remainingPageHeight - \@scaledImageHeight) < 4}
        \ifnumequal{\pgfmathresult}{1}{\pgfmathparse{\@scaledImageHeight - 4} \edef\@scaledImageHeight{\pgfmathresult}}{}
        %
        % Set up macro for all non-height specifications
        \def\@graphicOptions{}\relax%
        %
        % Set up dummy-keys for processing the \includegraphics-options
        \pgfkeys{%
            /dynimage/dummy/.cd,%
            height/.code = {\gdef\specifiesHeight{}},%
            /dynimage/dummy/.unknown/.code = {%
                % get unchanhed key-path
                \edef\keypath{\pgfkeyscurrentpath}%
                \IfBeginWith{\keypath}{/dynimage/dummy}{%
                    % trim away the local dummy path in order to get unchanged key-path
                    \StrBehind{\keypath}{/dynimage/dummy}[\keypath]
                    \xdef\keypath{\keypath}
                }{}%
                %
                % Add the respective key to \@graphicOptions but don't introduce equals where there shouldn't be any
                \edef\@val{##1}
                \ifdefequal{\@val}{\pgfkeysnovalue{}}{%
                    \xdef\@graphicOptions{\@graphicOptions,\keypath\pgfkeyscurrentname}
                }{%
                    \xdef\@graphicOptions{\@graphicOptions,\keypath\pgfkeyscurrentname=##1}%
                }%
            },%
        }%
        %
        % process the keys originally intended for the \includegraphics[]{imagefile} in order
        % to check for a possible height-definition. All other keys are stored in \@graphicOptions
        \pgfkeys{/dynimage/dummy/.cd, #1}%      
        %
        % Check if an explicit height for the image is given
        \ifdef{\specifiesHeight}{%
            % Create warning that the use of height prvented scaling
            \PackageWarning{dynimage}{Inserting image "#2" with specified height instead of adjusted height}%
            %
            % insert image as is
            \includegraphics[#1]{#2}
        }{%
            % log the scaled size of the image if in debug mode
            \iftoggle{@dynimageDebug}{%
                \PackageInfo{dynimage}{Inserting image "#2" with a height of \@scaledImageHeight pt}%
            }{}%
            %
            % add a height-specification to the keys for the image
            \xdef\@graphicOptions{height=\@scaledImageHeight pt,\@graphicOptions}
            %
            % Gobble away any leading and trailing commas as the keyvals-package used by
            % graphics can't deal with them
            \IfBeginWith{\@graphicOptions}{,}{%
                % removing leading comma
                \StrGobbleLeft{\@graphicOptions}{1}[\@graphicOptions]%
                \xdef\@graphicOptions{\@graphicOptions}%
            }{}%
            \IfEndWith{\@graphicOptions}{,}{%
                % remove trailing comma
                \StrGobbleRight{\@graphicOptions}{1}[\@graphicOptions]%
                \xdef\@graphicOptions{\@graphicOptions}%
            }{}%
            %
            % Fully expand options
            \edef\doPrintImage{\noexpand\includegraphics[\@graphicOptions]{#2}}%
            %
            % print out the scaled image
            \doPrintImage%
        }%
    }%
}

然后,您可以继续\usepackage{dynimage}在允许您使用\dynImage[<options>{<image>}宏的文档中使用。<option>是您想要传递给\includegraphics-command 的选项(注意:指定显式height将禁用任何缩放)并且<image>是应包含的图像文件的路径。

使用此宏代替\includegraphics将产生以下效果:
如果页面上有足够的空间,则将插入图像而不对其进行任何修改(如果直接使用则为\includegraphics)。但是,如果不是这种情况,则将计算一个缩放因子,图像必须使用该缩放因子进行缩放才能适合当前页面(就其高度而言)。如果缩放因子不低于最小缩放因子(可使用min scale factor包的 - 选项配置 - 默认值为0.75)并且如果选项中未指定任何明确说明height,则图像的高度将减小,以便它适合当前页面并填充它(可能除了几个点之外)。
如果您不介意图像缩小,可以将reckless- 选项传递给包,这将导致min scale factor设置为0.1

从我的问题中取出(稍微修改过的)MWE,然后将其用作

\documentclass{article}
\usepackage{graphicx}
\usepackage{lipsum}
\usepackage{duckuments}
\usepackage{showframe}
\usepackage{dynimage}

\begin{document}
    \lipsum[1-4]

    Here comes the image:

    \dynImage{example-image-duck}
%    \includegraphics{example-image-duck}

    \lipsum[4-7]
\end{document}

笔记

如您所见,该软件包尚未处理浮点数(包含标题)或类似构造。我打算扩展此软件包的功能以解决这些问题,并可能在某个时候更新此答案。

我遇到过几种情况,图像的大小设置为小于计算出的剩余大小,但它仍然被放在下一页。我不确定为什么会这样。我认为这要么是由于舍入问题,要么是由于计算剩余页面大小的方式不准确(或两者兼而有之)。这就是为什么我确保缩放后的图像大小始终至少小于4pt计算出的剩余页面高度。这似乎运行得相当好,但并不理想。
我也会研究这个问题,但如果你知道如何解决这个问题,我将非常感激你的评论 ;)

相关内容