Getting Bounding Coordinates for Multiple Hyperlinks in a Text Box in C#

Hi,

Problem: Hyperlink Position Data Needed for Rasterized Text Shapes

Background

We use Aspose.Slides to convert PowerPoint presentations into an HTML5 format for web playback. As part of this conversion, we rasterize shapes (including AutoShapes with text) into PNG images using GetImage() / SaveShapeThumbnail(). The rasterized images are then displayed in our HTML5 player.

When a shape contains hyperlinked text, we need to overlay invisible clickable regions on top of the rasterized image so that users can click on the correct part of the text to navigate to the intended URL. To do this, we need the bounding rectangle of each hyperlinked text portion within the shape, in coordinates relative to the shape itself.

The Problem

Currently, when a textbox (AutoShape) contains multiple hyperlinks across different text runs, we have no way to determine where each hyperlinked portion of text was rendered within the rasterized image. We only know:

  • The overall shape position and dimensions

  • Which text portions carry hyperlinks (via IPortion.PortionFormat.HyperlinkClick / HyperlinkMouseOver)

  • The hyperlink URLs and action types

We do not know:

  • The x, y position of each text portion within the rendered shape

  • The width and height of each text portion’s bounding box

Without this positional data, we cannot create accurate clickable regions. The result is that all hyperlinks in a multi-link textbox currently point to the same destination (we can only assign one clickable region to the entire shape image).

What We Need from the Aspose API

For a given IAutoShape (or any shape with an ITextFrame), after the shape’s layout has been computed, we need to obtain the bounding rectangle of individual text portions (IPortion) or at minimum individual paragraphs (IParagraph).

Specifically, for each IPortion that has a HyperlinkClick or HyperlinkMouseOver, we need:

Data Description
X Left edge of the text portion, relative to the shape’s top-left corner
Y Top edge of the text portion, relative to the shape’s top-left corner
Width Rendered width of the text portion
Height Rendered height of the text portion

These coordinates should reflect the final rendered layout – accounting for:

  • Font metrics (size, family, bold/italic)

  • Text wrapping and line breaks

  • Paragraph alignment (left, center, right, justified)

  • Paragraph spacing and indentation

  • Text margins/insets within the shape

  • Any transforms applied to the text frame

Questions for Aspose

  1. Does IParagraph.GetRect() return the bounding rectangle of a paragraph in shape-relative coordinates? If so, does it account for all layout factors (wrapping, alignment, spacing, margins)?

  2. Is there a way to get the bounding rectangle of an individual IPortion (text run) within a paragraph? We see references to IPortion.GetCoordinates() in some documentation – does this give us what we need? What coordinate system does it use?

  3. If portion-level coordinates are not available, is there a recommended approach to determine where a specific text run renders within a shape? For example:

  • Can we get the paragraph rectangle and then compute portion offsets using IPortion.GetRect() or similar?

  • Is there a way to query the text layout engine for glyph positions?

  1. Coordinate scale factor: When we call shape.GetImage(ShapeRenderingOptions, scaleFactor), the rasterized image is scaled. Do the paragraph/portion coordinates correspond to the unscaled shape dimensions, or do they account for the render scale? We need to map coordinates to the pixel space of the exported image.

  2. Multi-line portions: If a single text run wraps across multiple lines (e.g., a long hyperlinked text that breaks mid-word), does the API return multiple rectangles, or one bounding box encompassing all lines of that portion?

Example Scenario

A textbox contains:

TM255 Introduction to Computing (linked to URL-A)

blank line

T212 Electronics (linked to URL-B)

Introduction to Electronics (linked to URL-C)

blank line

T312 Advanced Electronics (linked to URL-D)

Electronic Applications (linked to URL-E)

After rasterization, we need to know that “TM255 Introduction to Computing” rendered at approximately (10, 5, 350, 22) within the shape image, “T212 Electronics” at (10, 48, 200, 22), and so on – so we can make each region independently clickable with its correct URL.

Current Aspose Version

We are using Aspose.Slides for .NET. Please advise which version (if any) supports the portion/paragraph coordinate APIs we need, or if this is a feature request.

Thanks

Internal ref: CONN-4169911

@bhav

Based on your description, here is a likely technical approach for Aspose.Slides.

Provisional answer

Aspose.Slides can give you the layout rectangle of a paragraph (IParagraph.GetRect()) and, starting with v23.5, the rectangle of an individual text run (IPortion.GetCoordinates()).

  • IParagraph.GetRect() returns a RectangleF that is relative to the top‑left corner of the shape and already takes the text‑frame margins, paragraph spacing, alignment, wrapping and the shape’s rotation into account.

  • IPortion.GetCoordinates() (or IPortion.GetRect() in older releases) also returns a RectangleF in the same shape‑relative coordinate system, but it represents only the part of the run that is on a single line. If the run wraps onto several lines the method returns the rectangle of the first line; you can call it for each line by iterating the Portion’s PortionLines collection (available from v23.7) which yields a separate rectangle for every line.

  • The coordinates are expressed in points (the same unit used by the slide). When you rasterize the shape with shape.GetImage(renderOptions, scaleFactor) or shape.SaveShapeThumbnail(...), you simply multiply the rectangle values by the same scaleFactor (or by renderOptions.Dpi/72) to obtain pixel coordinates that match the exported PNG.

If you are on a version prior to 23.5 the API does not expose portion‑level rectangles; the only workaround is to measure the text yourself (e.g., Graphics.MeasureString) while reproducing the paragraph’s wrapping rules, which is error‑prone.

What to try

// assume slide, shape already loaded
var autoShape = (IAutoShape)shape;
var textFrame = autoShape.TextFrame;

// paragraph rectangle (shape‑relative)
RectangleF paraRect = textFrame.Paragraphs[0].GetRect();

// portion rectangle (first line)
var portion = textFrame.Paragraphs[0].Portions[0];
RectangleF portionRect = portion.GetCoordinates();   // shape‑relative, points

// convert to pixel coordinates for a 2× scale factor
float scale = 2.0f;
Rectangle pixelRect = new Rectangle(
    (int)(portionRect.X * scale),
    (int)(portionRect.Y * scale),
    (int)(portionRect.Width * scale),
    (int)(portionRect.Height * scale));

If a portion spans multiple lines, iterate portion.PortionLines (v23.7+) and collect each line’s rectangle.


Clarifying questions

  1. Which exact version of Aspose.Slides for .NET are you currently using (e.g., 23.3, 23.5, 23.7, etc.)?
  2. When you rasterize the shape, do you use a custom scaleFactor/DPI in ShapeRenderingOptions, or do you rely on the default rendering? This determines how we should convert the point‑based rectangles to pixel coordinates.

@bhav,
Thank you for posting the questions.

Yes, IParagraph.GetRect() returns the bounding rectangle of a paragraph in shape-relative coordinates and takes into account all layout factors.

You shoud use IPortion.GetRect(). It also returns the bounding rectangle of a text portion in shape-relative coordinates. Aspose.Slides does not provide an API to get glyph positions.

The IParagraph.GetRect() and IPortion.GetRect() return one bounding box encompassing all lines of that paragraph and portion accordingly.

See also:
Get Paragraph Bounds from Presentations in .NET|Aspose.Slides Documentation
Manage Text Portions in Presentations in .NET|Aspose.Slides Documentation

We recommend that you use the latest version of Aspose.Slides for .NET.