*! version 3.1.1 21aug2026

Title

xlimage -- Insert or replace an image in an Excel workbook without
stacking copies and without Python

Syntax

xlimage using workbook.xlsx,
image(filename)
cell(cellref)
[sheet(name)]

Required
image(filename) image file to place (for example a .png
exported with graph export)

cell(cellref) anchor cell of the image, for example A3

Optional
sheet(name) worksheet holding the image; the first sheet by default

Description

xlimage places an image in an Excel workbook at the specified cell. If that
cell does not yet hold an image, the image is inserted; if one is already
anchored there, it is replaced. In both cases it avoids the common problem
with putexcel, whereby re-running a do-file does not overwrite images but
stacks new copies at the same position, inflating the file with invisible
duplicates.

Because of this, the same command line can be used on every run: the first
time it inserts and later runs replace, with no duplicated code and no stacked
images. The title, tables, formatting, and other images in the workbook are left
untouched.

Options

image(filename) is the image to place. Any format Excel supports is
allowed; a .png exported from a Stata graph is the typical case. When an
existing image is replaced, the new image keeps the position and size defined in
the workbook. Required.

cell(cellref) is the cell where the image is anchored (upper-left corner),
for example A3. It is case-insensitive. Required.

sheet(name) selects the worksheet. If omitted, the first sheet of the
workbook is used. Use it when the workbook has several sheets.

How it works

An .xlsx file is internally a ZIP archive of XML parts. xlimage
unzips it with unzipfile and, depending on the case: if an image is
already anchored at the cell, it overwrites only that internal image file
(leaving the rest of the structure untouched); if none is anchored there, it
adds the new anchor to the sheet's existing drawing, or creates the drawing,
relationships, and content-type entry needed to anchor the new image. It then
re-zips the archive with zipfile. When an existing image is replaced,
the anchor structure is not rewritten, so the operation cannot corrupt the
workbook.

Both Office anchor kinds (oneCellAnchor and twoCellAnchor) are
recognized, with or without a namespace prefix, as are workbooks with several
sheets and several images per sheet.

Requirements

Stata 14 or later (for unzipfile and zipfile). No Python or any
external package is required.

Remarks

The workbook must exist before calling xlimage: the command places images
into a workbook, it does not create the workbook itself. Create it first with
putexcel (which handles cells and formatting), then call xlimage
to place the graphs.

The workbook must not be open in Excel while the command runs, because the file
is locked and cannot be rewritten.

Both using() and image() accept absolute paths (for example
D:\reports\report.xlsx) as well as paths relative to the current
directory; the command resolves them internally, so it can be run from any
working directory.

This is a beta release, tested with workbooks produced by putexcel and
with templates saved by Excel. If a workbook has an unusual drawing structure and
the command does not place the image as expected, please report the case to the
author.

Examples

Prepare a template with a title and table (preserving its formatting):

. sysuse auto, clear

. putexcel set report.xlsx, sheet(Sheet1) modify

. putexcel A1 = "Price by origin"

. putexcel save

Place the graph: the same line works every time (inserts the first
time, replaces afterwards):

. graph box price, over(foreign)

. graph export g.png, replace width(1000)

. xlimage using report.xlsx, image(g.png) cell(A3) sheet(Sheet1)

On a later run with new data, use exactly the same line and the image is
replaced without stacking:

. graph box mpg, over(foreign)

. graph export g.png, replace width(1000)

. xlimage using report.xlsx, image(g.png) cell(A3) sheet(Sheet1)

Several images on one sheet: each anchor cell is independent, so replacing
one leaves the others in place:

. xlimage using report.xlsx, image(logo.png) cell(A1) sheet(Sheet1)

. xlimage using report.xlsx, image(g.png) cell(A20) sheet(Sheet1)

Author

Mario Anderson Apaza Ñaupa

Lima, Peru

Email: rioma310@gmail.com