【问题标题】:Figure sizes with pandoc conversion from markdown to docx从 markdown 到 docx 的 pandoc 转换的图形大小
【发布时间】:2013-02-12 09:57:59
【问题描述】:

我在 Rstudio 中使用 Rmarkdown 键入报告。在html用knitr转换时,还有一个由knitr生成的markdown文件。我用pandoc 转换这个文件如下:

pandoc -f markdown -t docx input.md -o output.docx

output.docx 文件很好,除了一个问题:图形的大小发生了变化,我需要在 Word 中手动调整图形的大小。有什么事情要做,也许是pandoc 的一个选项,以获得正确的数字大小?

【问题讨论】:

  • 您使用的是哪个版本的 Pandoc?如果使用过时的版本,可能的解决方法是在 knitr 内渲染较小的图像。
  • 这是版本 1.9.4.2。我不想更改 knitr 内部的大小,因为输出 html 文件中的大小很好。
  • 我现在已经试用了最新的 (Windows) Pandoc 版本。这不会改变任何事情。
  • 我很想找到这个问题的答案...
  • @TalGalili 请查看我使用 ImageMagick 的解决方案。

标签: image markdown docx knitr pandoc


【解决方案1】:

一种简单的方法是在各个块选项中包含比例因子k

{r, fig.width=8*k, fig.height=6*k}

以及全局块选项中的变量dpi

opts_chunk$set(dpi = dpi)

那么你可以在全局环境中编织Rmd文件之前设置dpik的值:

dpi <<- 96    
k <<- 1

或者您可以将它们设置在 Rmd 文件中的一个块中(例如在第一个块中设置 k)。

【讨论】:

    【解决方案2】:

    这是使用 R 脚本中的 ImageMagick 调整图形大小的解决方案。 70%的比例似乎是个不错的选择。

    # the path containing the Rmd file :
    wd <- "..."
    setwd(wd)
    
    # the folder containing the figures :
    fig.path <- paste0(wd, "/figure")
    # all png figures :
    figures <- list.files(fig.path, pattern=".png", all.files=TRUE)
    
    # (safety) create copies of the original files
    dir.create(paste0(fig.path,"_copy"))
    for(i in 1:length(figures)){
      fig <- paste0(fig.path, "/", figures[i])
      file.copy(fig,"figure_copy")
    }
    
    # resize all figures
    for(i in 1:length(figures)){
        fig <- paste0(fig.path, "/", figures[i])
        comm <- paste("convert -resize 70%", fig, fig)
        shell(comm)
    }
    
    # then run pandoc from a command line  
    # or from the pandoc() function :
    library(knitr)
    pandoc("MyReport.md", "docx")
    

    有关 ImageMagick 的resize 功能的更多信息:www.perturb.org

    【讨论】:

    • 顺便说一句,我已将 ImageMagick 添加为安装程序包中支持的软件之一。 cran.r-project.org/web/packages/installr
    • @TalGalili 谢谢。但是,我担心使用 convert -resize 70% fig1.png fig1.png 调整大小时会降低质量
    • 对不起,我的错,不知何故点击了错误。当我注意到它时,我无法恢复,太糟糕了。 @Tal Galili:请为我投票 + 作为补偿。 “质量损失”的论点是有效的。
    • 不,没有。像素图形缩放不好,我不建议在重新缩放后将其发送到一些高级期刊。
    • 是的,但这不值得。唯一完美的方法是创建 pdf 或 eps,然后将它们转换为最终大小。这将始终产生完美的输出(嗯,不是最终的 10x10 像素 :])。应该可以通过这种方式修改您的概念,但请注意,除了 Imagemagick 之外,您还需要安装 Ghostscript。
    【解决方案3】:

    我还想将 R markdown 转换为 html 和 .docx/.odt,其中的数字具有良好的尺寸和分辨率。到目前为止,我发现最好的方法是在 .md 文档中明确定义图形的分辨率和大小(dpi、fig.width 和 fig.height 选项)。如果你这样做,你就有很好的可用于发布的图表,并且 odt/docx 是可以的。如果您使用远高于默认 72 dpi 的 dpi,问题是图形在 html 文件中看起来太大。以下是我用来处理此问题的 3 种方法(注意,我使用带有 spin() 语法的 R 脚本):

    1) 在 knitr 选项中使用 out.extra ='WIDTH="75%"'。这将强制 html 的所有图形占据窗口宽度的 75%。这是一个快速的解决方案,但如果您的地块大小非常不同,则不是最佳解决方案。 (注意,我更喜欢使用厘米而不是英寸,因此到处都是 /2.54)

    library(knitr)
    opts_chunk$set(echo = FALSE, dev = c("png", "pdf"), dpi = 400,
                   fig.width = 8/2.54, fig.height = 8/2.54,
                   out.extra ='WIDTH="75%"'
    )
    
    data(iris)
    
    #' # Iris datatset
    summary(iris)
    boxplot(iris[,1:4])
    
    #+ fig.width=14/2.54, fig.height=10/2.54
    par(mar = c(2,2,2,2))
    pairs(iris[,-5])
    

    2) 使用 out.width 和 out.height 来指定图形在 html 文件中的像素大小。我使用常量“sc”将绘图的大小缩小为 html 输出。这是更精确的方法,但问题是对于每个图形,您必须同时定义 fig.witdth/height 和 out.width/height ,这真的很无聊!理想情况下,您应该能够在全局选项中指定例如out.width = 150*fig.width(其中 fig.width 从块到块变化)。也许这样的事情是可能的,但我不知道如何。

    #+ echo = FALSE
    library(knitr)
    sc <- 150
    opts_chunk$set(echo = FALSE, dev = c("png", "pdf"), dpi = 400,
                    fig.width = 8/2.54, fig.height = 8/2.54,
                    out.width = sc*8/2.54, out.height = sc*8/2.54
    )
    
    data(iris)
    
    #' # Iris datatset
    summary(iris)
    boxplot(iris[,1:4])
    
    #+ fig.width=14/2.54, fig.height=10/2.54, out.width= sc * 14/2.54, out.height= sc * 10/2.54
    par(mar = c(2,2,2,2))
    pairs(iris[,-5])
    

    请注意,对于这两种解决方案,我认为您不能使用 pandoc 将您的 md 文件直接转换为 odt (不包括数字)。我将 md 转换为 html,然后将 html 转换为 odt(没有尝试过 docx)。 类似的东西(如果以前的 R 脚本名称为“figsize1.R”):

    library(knitr)
    setwd("/home/gilles/")
    spin("figsize1.R")
    
    system("pandoc figsize1.md -o figsize1.html")
    system("pandoc figsize1.html -o figsize1.odt")
    

    3) 只需编译您的文档两次,一次使用低 dpi 值 (~96) 用于 html 输出,一次使用高分辨率 (~300) 用于 odt/docx 输出。这是我现在首选的方式。主要缺点是您必须编译两次,但这对我来说并不是问题,因为我通常只在工作结束时才需要 odt 文件来提供给最终用户。我在工作期间使用 Rstudio 中的 html 笔记本按钮定期编译 html。

    #+ echo = FALSE
    library(knitr)
    
    opts_chunk$set(echo = FALSE, dev = c("png", "pdf"), 
                   fig.width = 8/2.54, fig.height = 8/2.54
    )
    
    data(iris)
    
    #' # Iris datatset
    summary(iris)
    boxplot(iris[,1:4])
    
    #+ fig.width=14/2.54, fig.height=10/2.54
    par(mar = c(2,2,2,2))
    pairs(iris[,-5])
    

    然后用下面的脚本编译2个输出(注意这里你可以直接将md文件转换成html):

    library(knitr)
    setwd("/home/gilles")
    
    opts_chunk$set(dpi=96)
    spin("figsize3.R", knit=FALSE)
    knit2html("figsize3.Rmd")
    
    opts_chunk$set(dpi=400)
    spin("figsize3.R")
    system("pandoc figsize3.md -o figsize3.odt")
    

    【讨论】:

    • 好主意,谢谢。我会尽快尝试。欢迎访问该网站:)
    • 你好吉尔斯。我已经尝试了您的第三个解决方案,但这并没有改变大小。也许我没有正确遵循您的程序:我将opts_chunk$set 放在我的Rmd 文件的第一块中。在 html 输出中增加了大小,但在 docx 中没有增加(仅增加质量)。
    • 您尝试了 3 种解决方案中的哪一种?您是否在不同的代码块中指定了不同的fig.width/fig.height 和/或out.width/out.height
    • 第三种解决方案。是的,我总是在每个块中指定fig.widthfig.height(但不是out.widthout.height)。
    • 嗨。好的,我在编织之前没有运行opts_chunk$set。我下次试试。
    【解决方案4】:

    这是我的解决方案:破解由 Pandoc 转换的 docx,因为 docx 只是一组 xml 文件,调整图形大小非常简单。

    以下是从转换后的docx中提取的word/document.xml中的图形:

    <w:p>
      <w:r>
        <w:drawing>
          <wp:inline>
            <wp:extent cx="1524000" cy="1524000" />
            ...
            <a:graphic>
              <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
                <pic:pic>
                  ...
                  <pic:blipFill>
                    <a:blip r:embed="rId23" />
                    ...
                  </pic:blipFill>
                  <pic:spPr bwMode="auto">
                    <a:xfrm>
                      <a:off x="0" y="0" />
                      <a:ext cx="1524000" cy="1524000" />
                    </a:xfrm>
                    ...
                  </pic:spPr>
                </pic:pic>
              </a:graphicData>
            </a:graphic>
          </wp:inline>
        </w:drawing>
      </w:r>
    </w:p>
    

    因此,将节点wp:extenta:extcxcy 属性替换为所需的值将完成调整大小的工作。以下 R 代码适用于我。最宽的图形将占据变量out.width 指定的整行宽度,其余的则按比例调整大小。

    require(XML)
    
    ## default linewidth (inch) for Word 2003
    out.width <- 5.77
    docx.file <- "report.docx"
    
    ## unzip the docx converted by Pandoc
    system(paste("unzip", docx.file, "-d temp_dir"))
    document.xml <- "temp_dir/word/document.xml"
    doc <- xmlParse(document.xml)
    wp.extent <- getNodeSet(xmlRoot(doc), "//wp:extent")
    a.blip <- getNodeSet(xmlRoot(doc), "//a:blip")
    a.ext <- getNodeSet(xmlRoot(doc), "//a:ext")
    
    figid <- sapply(a.blip, xmlGetAttr, "r:embed")
    figname <- dir("temp_dir/word/media/")
    stopifnot(length(figid) == length(figname))
    pdffig <- paste("temp_dir/word/media/",
                    ## in case figure ids in docx are not in dir'ed order
                    sort(figname)[match(figid, substr(figname, 1, nchar(figname) - 4))], sep="")
    
    ## get dimension info of included pdf figures
    pdfsize <- do.call(rbind, lapply(pdffig, function (x) {
        fig.ext <- substr(x, nchar(x) - 2, nchar(x))
        pp <- pipe(paste(ifelse(fig.ext == 'pdf', "pdfinfo", "file"), x, sep=" "))
        pdfinfo <- readLines(pp); close(pp)
        sizestr <- unlist(regmatches(pdfinfo, gregexpr("[[:digit:].]+ X [[:digit:].]+", pdfinfo, ignore.case=T)))
        as.numeric(strsplit(sizestr, split=" x ")[[1]])
    }))
    
    ## resizing pdf figures in xml DOM, with the widest figure taking up a line's width
    wp.cx <- round(out.width*914400*pdfsize[,1]/max(pdfsize[,1]))
    wp.cy <- round(wp.cx*pdfsize[, 2]/pdfsize[, 1])
    wp.cx <- as.character(wp.cx)
    wp.cy <- as.character(wp.cy)
    sapply(1:length(wp.extent), function (i)
           xmlAttrs(wp.extent[[i]]) <- c(cx = wp.cx[i], cy = wp.cy[i]));
    sapply(1:length(a.ext), function (i)
           xmlAttrs(a.ext[[i]]) <- c(cx = wp.cx[i], cy = wp.cy[i]));
    
    ## save hacked xml back to docx
    saveXML(doc, document.xml, indent = F)
    setwd("temp_dir")
    system(paste("zip -r ../", docx.file, " *", sep=""))
    setwd("..")
    system("rm -fr temp_dir")
    

    【讨论】:

    • 你好。我还没有尝试过,但无论如何这看起来很棒(+1)。
    • @StéphaneLaurent 希望我的代码能够自我解释。这里只考虑pdf数字。由于缩小后的图形彼此保持相对大小,如果最宽的图形太大,其他图形在最终 docx 中看起来会非常小。请记住这一点。
    • PDF 数字?仅生成 png 图形。
    • @StéphaneLaurent,请查看我的更新代码。它现在可以处理 pdf 和非 pdf 数字,但最宽的仍然占据一整行宽度,其余的则按比例调整大小。
    猜你喜欢
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 2020-05-04
    • 2012-11-11
    • 2014-10-06
    • 2021-05-26
    • 2013-01-14
    相关资源
    最近更新 更多