> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/ios/annotation/custom-annot-appearance.md).

# Create custom annotations on iOS

Learn how to prevent the loss of changes made to a \`PTAnnot\` appearance in PDFTron by avoiding calling RefreshAppearance. Find out more here! The Apryse iOS SDK streamlines secure document processing

It's possible to modify an annotation's appearance directly using PDFNet.

The sample code below demonstrates how to replace an annotation's appearance with a custom image.

{% tabs %}
{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
func setCustomImage(annot:PTAnnot, doc:PTPDFDoc)
{
    // Initialize a new PTElementWriter and PTElementBuilder
    let writer: PTElementWriter =  PTElementWriter()
    let builder: PTElementBuilder = PTElementBuilder()
    writer.writerBegin(with: doc.getSDFDoc()!, compress: true)
    
    // Initialize the new image
    let image = PTImage.create(doc.getSDFDoc(), filename: Bundle.main.path(forResource: "image", ofType: "png"))
    guard let w = image?.getWidth(), let h = image?.getHeight() else {
        print("Unable to get image dimensions")
        return
    }
    
    // Initialize a new image element
    let element: PTElement = builder.createImage(withCornerAndScale: image, x: 0, y: 0, hscale:Double(w), vscale: Double(h))
    
    // Write the element
    writer.writePlacedElement(element)
    
    // Get the bounding box of the new element
    let bbox: PTPDFRect = element.getBBox()

    // Configure the appearance stream that will be written to the annotation
    let new_appearance_stream: PTObj = writer.end()

    // Set the bounding box to be the rect of the new element
    new_appearance_stream.putRect(
        "BBox",
        x1: bbox.getX1(),
        y1: bbox.getY1(),
        x2: bbox.getX2(),
        y2: bbox.getY2())
        
    // Overwrite the annotation's appearance with the new appearance stream
    annot.setAppearance(new_appearance_stream, annot_state:  e_ptnormal, app_state: nil)
}
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
- (void)setCustomImage:(PTAnnot*)annot doc:(PTPDFDoc*)doc
{
    // Initialize a new PTElementWriter and PTElementBuilder
    PTElementWriter* writer = [[PTElementWriter alloc] init];
    PTElementBuilder* builder = [[PTElementBuilder alloc] init];
    
    [writer WriterBeginWithSDFDoc:[doc GetSDFDoc] compress:YES];

    // Initialize the new image
    PTImage* img = [PTImage Create:[doc GetSDFDoc] filename:[[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"]];
    int w = [img GetImageWidth], h = [img GetImageHeight];

    // Initialize a new image element
    PTElement* img_element = [builder CreateImageWithCornerAndScale:img x:0 y:0 hscale:w vscale:h];

    // Write the element
    [writer WritePlacedElement:img_element];

    // Get the bounding box of the new element
    PTPDFRect* bbox = [img_element GetBBox];

    // Configure the appearance stream that will be written to the annotation
    PTObj* appearance_stream = [writer End];

    // Set the bounding box to be the rect of the new element
	[appearance_stream PutRect:@"BBox" x1:[bbox GetX1] y1:[bbox GetY1] x2:[bbox GetX2] y2:[bbox GetY1]];

    // Overwrite the annotation's appearance with the new appearance stream
	[annot SetAppearance:appearance_stream annot_state:e_ptnormal app_state:0];
}
```

{% endcode %}
{% endtab %}

{% tab title="C#" %}
{% code lineNumbers="true" %}

```csharp
public void SetCustomImage(Annot annot, PDFDoc doc, string imagePath)
{
    // Initialize a new ElementWriter and ElementBuilder
    ElementWriter writer = new ElementWriter();
    ElementBuilder builder = new ElementBuilder();
    writer.Begin(doc.GetSDFDoc(), true);
    // Initialize the new image
    Image image = Image.Create(doc.GetSDFDoc(), imagePath);
    int w = image.GetImageWidth();
    int h = image.GetImageHeight();
    // Initialize a new image element
    Element element = builder.CreateImage(image, 0, 0, w, h);
    // Write the element
    writer.WritePlacedElement(element);
    // Get the bounding box of the new element
    Rect bbox = new Rect();
    element.GetBBox(bbox);
    // Configure the appearance stream that will be written to the annotation
    Obj new_appearance_stream = writer.End();
    // Set the bounding box to be the rect of the new element
    new_appearance_stream.PutRect(
        "BBox",
        bbox.x1,
        bbox.y1,
        bbox.x2,
        bbox.y2);
    // Overwrite the annotation's appearance with the new appearance stream
    annot.SetAppearance(new_appearance_stream);
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code lineNumbers="true" %}

```java
public void setCustomImage(Context context, Annot annot, PDFDoc doc, int imageRes) throws PDFNetException {
    // Initialize a new ElementWriter and ElementBuilder
    ElementWriter writer = new ElementWriter();
    ElementBuilder builder = new ElementBuilder();
    writer.begin(doc.getSDFDoc(), true);
    File imageFile = Utils.copyResourceToLocal(context, imageRes, "image", ".png");
    // Initialize the new image
    Image image = Image.create(doc.getSDFDoc(), imageFile.getAbsolutePath());
    int w = image.getImageWidth();
    int h = image.getImageHeight();
    // Initialize a new image element
    Element element = builder.createImage(image, 0, 0, w, h);
    // Write the element
    writer.writePlacedElement(element);
    // Get the bounding box of the new element
    Rect bbox = element.getBBox();
    // Configure the appearance stream that will be written to the annotation
    Obj new_appearance_stream = writer.end();
    // Set the bounding box to be the rect of the new element
    new_appearance_stream.putRect(
        "BBox",
        bbox.getX1(),
        bbox.getY1(),
        bbox.getX2(),
        bbox.getY2());
    // Overwrite the annotation's appearance with the new appearance stream
    annot.setAppearance(new_appearance_stream);
}
```

{% endcode %}
{% endtab %}

{% tab title="Kotlin" %}
{% code lineNumbers="true" %}

```kotlin
@Throws(PDFNetException::class)
fun setCustomImage(context: Context, annot: Annot, doc: PDFDoc, imageRes: Int) {
    // Initialize a new ElementWriter and ElementBuilder
    val writer = ElementWriter()
    val builder = ElementBuilder()
    writer.begin(doc.sdfDoc, true)
    val imageFile = Utils.copyResourceToLocal(context, imageRes, "image", ".png")
    // Initialize the new image
    val image = Image.create(doc.sdfDoc, imageFile!!.absolutePath)
    val w = image.imageWidth
    val h = image.imageHeight
    // Initialize a new image element
    val element = builder.createImage(image, 0.0, 0.0, w.toDouble(), h.toDouble())
    // Write the element
    writer.writePlacedElement(element)
    // Get the bounding box of the new element
    val bbox = element.bBox
    // Configure the appearance stream that will be written to the annotation
    val new_appearance_stream = writer.end()
    // Set the bounding box to be the rect of the new element
    new_appearance_stream.putRect(
            "BBox",
            bbox.x1,
            bbox.y1,
            bbox.x2,
            bbox.y2)
    // Overwrite the annotation's appearance with the new appearance stream
    annot.appearance = new_appearance_stream
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

In addition to images, an annotation's appearance can be created using (nearly) everything the PDF specification allows, including vector content and text. For an example of how to create PDF vector and text content, see the [ElementBuilder sample ](/ios/get-started/samples/elementbuildertest.md).

{% hint style="warning" %}
**Warning**

If you call [RefreshAppearance](https://sdk.apryse.com/api/ios/Classes/PTAnnot.html#/c:objc\(cs\)PTAnnot\(im\)RefreshAppearance) after modifying the appearance of a \`PTAnnot\`, then the annotation's default appearance will be restored and any changes you have made will be lost.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/ios/annotation/custom-annot-appearance.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
