About Part Relationships

Understanding Part Relationships

An Open XML package part can have one or more relationships to other parts in the package. For example, a slide layout has a relationship to the slide master it inherits properties from. And if the slide layout includes an image, such as a company logo, it will also have a relationship to the image part corresponding to that logo’s image file.

Conceptually, a part relationship might be stated:

Part x is related to part y of type z.

While these relationships are reciprocal in a way of thinking (e.g. part y is also related to part x), in Open XML they are generally defined in one direction only. For example, a slide may have a relationship to an image it contains, but an image never has a stated relationships to a slide. One exception to this is slide masters and slide layouts. A slide layout has a relationship to the slide master it inherits from at the same time the slide master has a relationship to each of the slide layouts that inherit from it.

In the example above, part x can be referred to as the source part or from-part, part y as the target part or to-part, and type z as the relationship type. The relationship type essentially ends up being the part type of the target part (e.g. image, slide master, etc.).

Inbound and outbound relationships

Each relationship is outbound for its source part and inbound for its target part. The outbound relationships of a part are those found in its part relationship item, commonly referred to as its “rels file”. Only outbound relationships are recorded in the package. Inbound relationships are purely abstract, although they can be inferred from outbound relationships if an application had a need for them.

Not all parts can have outbound relationships. For example, image and presentation properties parts never have outbound relationships. All parts, however, participate in at least one inbound relationship. Any part with no inbound relationships could be removed from the package without consequence, and probably should be. For example, if upon saving it was noticed that a particular image had no inbound relationships, that image would be better not written to the package. (Makes me wonder whether loading a package by walking its relationship graph might be a good idea in some instances.)

Direct and indirect relationship references

Each relationship is recorded as a relationship element in the rels file belonging to a specific part. (The package relationship item /_rels/.rels is the only exception, it belongs to the package, not to any part). Each relationship entry specifies a part-local relationship id (rId) for the relationship, the relationship type (essentially the part type of the target, e.g. slide, image), and a path the target part file. The source part is not explicitly stated in the relationship entry, it is implicitly the part the .rels file belongs to.

These can be thought of as external relationship references in that the rels file is a separate package item, external to the part itself. However, in certain cases, a relationship may be referenced within the XML of the from-part. These can be thought of as internal relationship references.

As an example of where an internal relationship reference is required, consider a slide containing images of three different products. Each picture has a corresponding <p:pic> element in the slide’s shape tree, each of which must specify the particular image file it displays, distinguishing it from the other two image files related to the slide.

The picture elements specify which of the related images to display using the part-local relationship id (rId) matching the required image, ‘rId2’ in the example below:

<p:blipFill>
  <a:blip r:embed="rId2">
    ...
  </a:blip>
</p:blipFill>

Which is an indirect reference to image1.png specified as the target of ‘rId2’ in the slide’s rels file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://.../relationships">
  <Relationship Id="rId1" Type="http://.../slideLayout" Target=".../slideLayout2.xml"/>
  <Relationship Id="rId2" Type="http://.../image" Target=".../image1.png"/>
</Relationships>

This indirection makes sense as a way to limit the coupling of presentation parts to the mechanics of package composition. For example, when the XML for the slide part in the example above is being composed, the slide can determine the reference to the image it’s displaying without reference outside its own natural scope. In contrast, determining the eventual location and filename of that image in any particular package that was saved would require the slide code to have visibility into the packaging process, which would prevent packaging being delegated to a separate, black-box module.

Implicit and explicit relationships

There is also a distinction between implicit and explicit relationships which is described in the spec (ECMA-376-1) in section 9.2. I haven’t encountered those yet in the context of PresentationML (the spec uses an example of footnotes from WordprocessingML), so I do not discuss them here.

Relationship Mechanics

Relationship life-cycle

A representation of a relationship must operate effectively in two distinct situations, in-package and in-memory. They must also support lifecycle transitions from in-package to in-memory and from in-memory back to in-package.

Abstract model

Each relationship has the following abstract attributes

source

The “from-part” of the relationship.

id

Source-local unique identifier for this relationship. Each source part’s relationship ids should be a sequence of consecutive integers starting from 1.

target_type

The content type of the relationship’s to-part.

target

A direct reference to the relationship’s to-part.

Maintaining relationships by dynamic parts (e.g. Slides)

How will dynamic parts (like Slide) interact with its relationship list?

? Should it just add items to the relationship list when it creates new things?

? Does it need some sort of lookup capability in order to delete? Or just have a delete relationship method on _Relationships or something like that.

Need to come up with a plausible set of use cases to think about a design. Right now the only use case is loading a template into a presentation and saving a presentation.

  • Add an image to a slide.
  • Change a slide’s slide layout
  • comment, notesSlide, tag, image, and slideLayout are the only outbound relationship types for a slide, although I expect there are some other DrawingML bits I haven’t accounted for yet.

On reflection I’m thinking there’s not too much urgency on noodling this out too far, the present construction should work fine for now and be able to be extended without disrupting other code too much.

SCRAP

Rationale for Relationship to be an association class

When loaded into memory, each relationship target must be a reference to an active part object (or at least a part key that can be resolved to a reference, but why do this lookup multiple times?). This is both because those relationships can change and also because the package path, while it can be calculated at runtime, is not guaranteed to be stable (e.g. a new slide can be inserted between two existing ones) and is not finally resolved until the presentation is saved.

General description of package relationships items

  • Relationships items specify the relationships between parts of the package, although they are not themselves a part.
  • All relationship items are XML documents having a filename with the extension ‘.rels’ located in a directory named ‘_rels’ located in the same directory as the part.
  • The package relationship item has the URI ‘/_rels/.rels’.
  • Part relationship items have the same filename as the part whose relationships they describe, with the ‘.rels’ extension appended as a suffix. For example, the relationship item for a part named /ppt/slides/slide1.xml would have the URI /ppt/slides/_rels/slide1.xml.rels.