How to Use IronWord with Google Antigravity

Curtis Chau
Curtis Chau
Updated: 2026年10月19日

Field engineers submit inspection reports as Word files, and the embedded photos are the evidence. A review page that builds without errors still fails when it drops photos, omits images within tables, assigns incorrect captions, or reports inaccurate photo sizes. A build log records none of these failures.

Google Antigravity can test the page the way a reviewer does. A sandboxed browser subagent, started with the /browser command, opens the page, picks each report in the file control and takes a screenshot of the result. On the server, IronWord opens the uploaded DOCX files and returns their photos through ExtractImages, with no Microsoft Office installation. The review page below is built with both tools and tested against six reports with known photo counts, captions and pixel sizes.

The guide is for developers who have AI agents write .NET code that reads Word documents. Generating the reports themselves with an AI model is out of scope.

Quickstart
NuGetNuGetでインストール

PM > Install-Package IronWord

Install IronWord by running the command above in the NuGet Package Manager Console, or search for the package in the NuGet Package Manager.

Describe the review page in a /plan prompt once Antigravity has the Blazor project open.

/plan Add a report review page at /photos to this Blazor Web App on .NET 9.
The app uses the Interactive Server render mode.

Here's what the page needs:

- A reviewer picks a site inspection report (.docx) with InputFile.
- The server opens it with IronWord and calls ExtractImages.
- Add the IronWord package from NuGet, pinned to 2026.10.4. Its namespace is IronWord.
- Each photo appears as a thumbnail with its width and height in pixels and its image format.
- The page also shows how many photos the report holds.
- Link the page from NavMenu.

Read IRONWORD_LICENSE_KEY from the environment once at startup into IronWord.License.LicenseKey.
The app reads that variable itself.
Only check that it's set and how long it is.
Never print, log or store its value.
Don't list environment variables either.

Answer these in the Implementation Plan:

- which render mode the page gets
- how the upload stream reaches IronWord
- the size limit passed to OpenReadStream

You're done when dotnet build reports no errors.
Text

A successful build answers none of the three questions the plan must cover. Microsoft's render modes guide explains when a page is interactive, and the Blazor file upload guide covers upload handling and size limits.

Provide the agent with the variable name, but do not share its value. IronWord also reads an IronWord.LicenseKey entry in appsettings.json, a file that a commit can include by mistake. The license key guide lists all locations IronWord checks for the key.



What is Google Antigravity, and how is it different from a chat assistant?

Google's overview describes the Antigravity app as a desktop application that launches, orchestrates and inspects teams of agents. An agent runs commands, reads and writes files and controls a sandboxed Chrome browser. A chat assistant replies with text, while an agent returns Artifacts, for example an Implementation Plan.

In a slopsquatting attack, someone registers a package under a name that AI agents produce for libraries that were never published, and an agent that requests that name installs the attacker's code. Name the IronWord package in every task that involves packages. The hub AI coding assistants guide lists IronWord's documentation files for agents and contains a one-prompt acceptance test.

What should you check in the plan, the code changes and the Walkthrough?

Beyond the three answers requested by the Quickstart prompt, the Implementation Plan should state the message produced for a report without photos. Among the code changes, find the line that computes the pixel size, and check each IronWord member against the API reference.

Each report needs its own screenshot in the Walkthrough, next to the results table. Antigravity saves screenshots as image artifacts, and a reader can comment on any of them. A comment on a screenshot that shows a caption under the wrong photo tells the agent which image to fix.

How do you test the IronWord review page in a real browser?

Give /browser a results table whose cells it fills only by uploading each file.

/browser Start the site with dotnet run and open /photos.
Its http profile listens on http://localhost:5036.

Take each .docx file in TestData/reports through the page's file picker, one at a time:

1. Pick the file.
2. Wait for the photo list or an error message.
3. Take a screenshot.

TestData/reports/expected.csv lists each report's site photos in document order.
It gives their number, captions and pixel sizes, with | between entries.

Build a table in the Walkthrough with these columns:

- file
- bytes
- photos expected
- thumbnails shown
- pixel sizes expected
- pixel sizes shown
- pass or fail

For a failed row, quote the server log entry or browser console error behind it.
Do that before you change any code.
If neither recorded one, write that down.
After a fix, restart the app and put all six reports through the page again.
Stop the app when you finish.
Text

The six reports cover the layouts that a photo page must handle. One contains a PNG, a JPEG, and a GIF, and one stores photos in table cells. Another includes a logo in the page header, one places the same image twice, one contains no photos, and one is a 7.7 MB report with four 12 MP photos.

ご注意: Review the screenshot of the full-resolution report first, because it shows whether the increased limit took effect.

What causes missing or wrong photos?

In our tests, every failed row resulted from one of three causes. To identify them, the extraction code from IronWord's extract images guide was copied into an upload handler in a new .NET 9 Blazor project, and the six reports were processed through headless Chrome.

What the page showsCauseFix
No output for any reportStatic rendering@rendermode InteractiveServer
No output, and the server log remains emptyThe document constructor reads the upload stream synchronouslyLoadAsync on the upload stream
An error for the full-resolution report onlyThe 512,000-byte default upload limitA limit above the largest report

The second row is the most difficult to diagnose. The constructor threw NotSupportedException with the message "Synchronous reads are not supported." The exception became visible only when the handler caught and logged it.

How do you set up a project Antigravity can work in?

Give Antigravity a repository that already builds and serves a page. The test project came from dotnet new blazor -int Server -f net9.0 and built with 0 errors before any prompt ran. The template writes a random port into the http profile of Properties/launchSettings.json, so copy that URL into the /browser prompt.

The next task audits the package and the license key.

Which IronWord version does this project reference? Quote its PackageReference line.
Then point me to the line that applies the license key.

Check the project against these key rules:

- The key reaches the app only through IRONWORD_LICENSE_KEY at startup.
- Neither appsettings.json nor appsettings.Development.json has an IronWord.LicenseKey entry.
- Properties/launchSettings.json doesn't carry the key.
- A missing variable at startup gets one logged warning that names it and shows no value.

Edit Program.cs only if that warning doesn't exist yet.
Leave every other file as it is.
Tell me which rules pass and which fail.
Text

Quote the reported version in later tasks. IronWord 2026.10.4 opened every report and extracted every photo without any license key. Saving threw a LicensingException that read "Production License Required". A missing key therefore becomes apparent only when the app first writes a file, which this read-only page never does.

How do you apply the page's rules to later tasks?

After all six rows pass, record what the session decided. Antigravity's rules documentation says that each time the agent reads or edits a file, it loads any GEMINI.md or AGENTS.md it finds in each folder between that file and the workspace root. /learn converts feedback from the session into persistent project rules.

/learn Create AGENTS.md at the root of the repository and record what this session settled.
Put the notes under the heading "Inspection report photos". They should cover:

- the render mode the /photos page needs
- the upload limit, with the reason for its value
- how the upload reaches IronWord without a synchronous read
- where a photo's pixel size comes from
- that IRONWORD_LICENSE_KEY stays out of every file
- that no page change is done until all six reports in TestData/reports pass the /browser table
Text

Once the file exists, the render mode and the upload limit are recorded in one location that every task reads. The agent does not update that file automatically. Request the update in the same task that changes the value.

How do you load an uploaded report with IronWord?

Specify the result and the evidence, and allow the agent to structure the code.

/plan Write the Implementation Plan Artifact before you edit anything.

Move the IronWord code out of the page into Services/ReportPhotoService.cs.
Register it as a singleton.

The service works like this:

- It opens the upload with WordDocument.LoadAsync, straight from OpenReadStream with a 30 MB limit.
- It checks the file's Size first and turns away anything larger before it opens a stream.
- A refused file gets a clear message that the page shows.
- A second public method takes an already loaded WordDocument.
- Tests can call that method without a browser.

For each photo, the service returns:

- the pixel size
- the size the photo is placed at in the report, in inches
- the format
- a JPEG thumbnail no wider than 320 pixels
- the caption

A caption is the first paragraph with text after the photo's own paragraph.
For a photo inside a table cell, it's the text of the cell directly below.

Show each caption under its thumbnail.
Then repeat the /browser table with a captions column checked against expected.csv.
Text

Expect a service similar to this example. It compiles with IronWord 2026.10.4, and the calibration test page using it correctly listed the photo count, captions, and pixel sizes for all six reports.

using IronSoftware.Drawing;
using IronWord;
using IronWord.Models;
using Microsoft.AspNetCore.Components.Forms;

public sealed record ReportPhoto(int PixelWidth, int PixelHeight, double WidthPoints, double HeightPoints, string Format, string Caption, string ThumbnailDataUrl);

public sealed class ReportPhotoService
{
    public const long MaxUploadBytes = 30 * 1024 * 1024;

    public async Task<List<ReportPhoto>> ReadAsync(IBrowserFile file)
    {
        if (file.Size > MaxUploadBytes)
            throw new IOException($"{file.Name} is {file.Size:N0} bytes, over the {MaxUploadBytes:N0} byte limit.");
        await using var upload = file.OpenReadStream(MaxUploadBytes);
        var doc = await WordDocument.LoadAsync(upload);
        return Read(doc);
    }

    public static List<ReportPhoto> Read(WordDocument doc)
    {
        var photos = new List<ReportPhoto>();
        var body = doc.Paragraphs.Where(p => p.Parent is DocumentSection).ToList();
        for (int i = 0; i < body.Count; i++)
        {
            foreach (var img in body[i].ExtractImages())
            {
                var caption = body.Skip(i + 1).Select(p => p.ExtractText().Trim()).FirstOrDefault(t => t.Length > 0) ?? "";
                photos.Add(Make(img, caption));
            }
        }
        foreach (var table in doc.Tables)
            for (int r = 0; r < table.Rows.Count; r++)
                for (int c = 0; c < table.Rows[r].Cells.Count; c++)
                    foreach (var img in ((TableCell)table.Rows[r].Cells[c]).ExtractImages())
                    {
                        var caption = r + 1 < table.Rows.Count && c < table.Rows[r + 1].Cells.Count
                            ? ((TableCell)table.Rows[r + 1].Cells[c]).ExtractText().Trim() : "";
                        photos.Add(Make(img, caption));
                    }
        return photos;
    }

    static ReportPhoto Make(ImageContent img, string caption)
    {
        var bmp = (AnyBitmap)img.Image;
        int tw = Math.Min(320, bmp.Width);
        int th = Math.Max(1, bmp.Height * tw / bmp.Width);
        using var thumb = new AnyBitmap(bmp, tw, th);
        var jpg = thumb.ExportBytes(AnyBitmap.ImageFormat.Jpeg, 80);
        return new ReportPhoto(bmp.Width, bmp.Height, img.Width, img.Height, img.Type.ToString(), caption,
            "data:image/jpeg;base64," + Convert.ToBase64String(jpg));
    }
}
C#

The size check uses the value reported by the browser, so an oversized file fails before the upload stream opens. Microsoft's upload guide advises against relying solely on this client-supplied value, and the limit set in OpenReadStream enforces the actual cap. Reading the full-resolution report took 1,118 ms and 1,280 ms in two calibration runs. To avoid large data transfers, the service generates scaled thumbnails instead of sending full-size images to the browser.

Which images does IronWord extract from a document?

The ExtractImages method returns photos located in the document body and in table cells. It skips the logo in the page header, so a letterhead does not count as a site photo. If a photo appears multiple times, it is returned once for each placement, each with its own size.

Each result includes its width and height in points. For example, a 3-inch-wide photo is reported as 216 points, regardless of resolution. The sample service retains these point values. To convert to inches, divide by 72 or use GetWidth(MeasurementUnit.Inch), as demonstrated by the agent. The pixel size is obtained from the decoded image, either by casting the Image property to AnyBitmap or by decoding the Base64 string in ImageData.

The paragraph list includes paragraphs within table cells in reading order, so selecting the next paragraph with text assigns the first photo's caption to the second table photo. The document's ExtractText also omits table text, so its output holds no table captions.

When should you dispatch more than one agent?

Split the checks between two agents. One tests the service in code. The other replays the reviewer's uploads in the browser.

Agent A: add an xUnit project named InspectionPhotos.Tests.
Its tests take each report in TestData/reports through these steps:

1. Load the report from disk with WordDocument.
2. Pass it through the extraction code in ReportPhotoService.
3. Assert the photo count, pixel sizes and captions that expected.csv lists, in document order.

Add one more test proving a file above the upload limit is refused before IronWord opens it.
expected.csv quotes any field that holds a comma, so parse it as CSV.
The web project is at the repository root, so keep the test folder out of its compile items.
Run the tests with dotnet test InspectionPhotos.Tests.

Agent B: wait until Agent A's tests are green on the current code.
Then put the six reports through /photos with /browser.
Attach one screenshot per report to the Walkthrough.
Note there that the tests ran first.
Text

Tests that open reports from disk need neither a browser nor a running application, so they detect a caption regression before a /browser pass.

ご注意: In the desktop app, each agent can run as a separate task. Compare the two Walkthroughs before you merge.

How do you deploy the page to a Linux container?

Besides its managed assemblies, IronWord 2026.10.4 contains a native layout library built for Windows (x64 and x86), linux-x64 and macOS.

This page will run in a linux-x64 container based on mcr.microsoft.com/dotnet/aspnet:9.0.
Leave the C# untouched.
Docker is missing on this machine, so don't run docker build.

Work through these steps:

1. Write a Dockerfile whose build stage publishes a linux-x64 build.
2. Have the image take IRONWORD_LICENSE_KEY from the container environment when it starts.
3. Run dotnet publish -c Release -r linux-x64 --self-contained false -o out/linux.

Then list every native library (.so) in out/linux with the NuGet package that supplied it.
If there's none, say so.
Text

Docker is not needed to list the native files, because the publish folder on Windows holds the same files the image receives. Publishing the calibration app for linux-x64 produced one native file, libLayoutEngineInterop.so (8,303,240 bytes). That file links only against glibc, and the package has no musl or linux-arm64 build, so this release has no native library for Alpine or ARM64 Linux images. On Windows, loading and extracting photos did not load the native library. IronWord's Docker guide recommends using Ubuntu, Debian, or RHEL images.

What did the prompts produce in a real run?

One Antigravity CLI session ran all seven prompts unattended and skipped every permission prompt. The app read the license key only from IRONWORD_LICENSE_KEY. We uploaded each report in headless Chrome twice, once on the code from the first prompt and once on the final code.

  • Photo counts and pixel sizes were correct for all six reports in both Chrome checks.
  • Captions went from none after the first prompt, which asked for none, to 12 of 12 correct in the final application.
  • Release builds of the application and the test project reported 0 warnings and 0 errors.
  • All seven xUnit tests passed.
  • The linux-x64 publish included one native library.
  • Each of the three browser passes processed all six reports through the file picker.
  • Some steps ran differently from the page. The Quickstart prompt already set the 30 MB limit and copied each upload into memory, so none of the three causes above appeared. Each /plan prompt stopped after its plan until an approval arrived. Agent A and Agent B ran in one turn of the same conversation. Thumbnails were resized with SixLabors.ImageSharp, a transitive IronWord dependency. The agent committed the first browser pass's screenshots and the out/linux publish folder.
  • /learn wrote no AGENTS.md. Its Artifact listed all six points and asked for confirmation first.

The ChatGPT Codex guide processes a whole folder of Word documents in parallel under a concurrency limit, where this page handles one upload at a time. The Claude Code guide approves a design in plan mode before any file changes, as the plan-first prompts do here.

The Antigravity CLI version, model, duration and test machine for this run are in the hub's run record.


Troubleshooting


Questions?

If you have any questions, reach out to support@ironsoftware.com

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

詳しく読む

準備はできましたか?

Nuget Downloads 58,016バージョン:2026.10リリースされたばかり