Search code examples
iosswiftimageviewimage-resizing

ImageView changes coordinates when I draw on that swift ios


I am following this tutorial, at the first when app launches everything is alright imagView is set where I set it. But the problem is when I start drawing on imageView the size of imageView resizes to ambiguous coordinates and it enlarges and occupy to much space result in ambiguous view, even I have set the auto layout. May be I am missing something, here is the code :

import UIKit
import CoreImage

class ViewController: UIViewController {

let imageNameIs = "BradPitImage"
var lastPoint = CGPoint.zero
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false

@IBOutlet weak var tempImageView: UIImageView!
var tempImageViewtemp : UIImageView = UIImageView()

@IBOutlet weak var resetButton: UIButton!
@IBOutlet weak var saveButton: UIButton!

@IBAction func resetAct(_ sender: Any) {
    tempImageView.image = nil
    tempImageView.image = UIImage(named: imageNameIs)
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.tempImageView.image = UIImage(named: imageNameIs)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = false
    if let touch = touches.first {
        lastPoint = touch.location(in: self.tempImageView)//view)
    }
}

func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
    // 1
    UIGraphicsBeginImageContext(view.frame.size)//(tempImageView.frame.size) makes lines coluored of image
    let context = UIGraphicsGetCurrentContext()
    tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))

    // 3
    context!.move(to: fromPoint)
    context!.addLine(to: toPoint)
    context!.setLineCap(.round)
    context!.setLineWidth(brushWidth)
    context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
    context!.setBlendMode(.normal)

    // 4
    context!.strokePath()
    //CGContextStrokePath(context!)

    // 5
    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    tempImageView.alpha = opacity
    UIGraphicsEndImageContext()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = true
    if let touch = touches.first {
        let currentPoint = touch.location(in: view)
        drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
    // 7
    lastPoint = currentPoint
    }
}

 func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    // 6
    swiped = true
    if let touch = touches.first as? UITouch {
        let currentPoint = touch.location(in: view)
        drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
        // 7
        lastPoint = currentPoint
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    if !swiped {
        //draw a single point
        drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
    }
    UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
    tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
    tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
    tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
  }
}

Here are images:

1) When Launches

enter image description here

2) When I start drawing

enter image description here

**Update According to DonMag's answer still ambiguous coordinates **

it work only for portrait orientation , the problem is when I do landscape mode and start drawing then image size reduces to top and there is increasing wide are empty at bottom, and then I do portrait then image became at the centre from top to bottom as more narrower I have added the screenshots new according to your code

1) from portrait to landscape at first time :

enter image description here

2) when I start drawing, it reduces the image and drawing becomes blur

enter image description here

Now when launches from landscape looks like this and drawing does does not reduces nor makes any blur:

enter image description here

But when turning to portrait image shrinks like this

enter image description here


Solution

  • Couple things...

    1. It looks like you have your image view set to Aspect Fit scale. That means your image is not the same size as the image view itself.

    2. You are mixing frame sizes of your image view and the root view.

    Here is your code with a few modifications. Review the comments indicated by // ---. You should be able to replace your current code and run this as-is:

    import UIKit
    import CoreImage
    
    extension UIView {
        // return content of view as a UIImage
        // can be used to get a copy of the rendered image view
        // e.g. the result of scaling setting
    
        func asImage() -> UIImage {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            return renderer.image { rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
        }
    
    }
    
    class ViewController: UIViewController {
    
        let imageNameIs = "BradPitImage"
        var lastPoint = CGPoint.zero
        var red: CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue: CGFloat = 0.0
        var brushWidth: CGFloat = 10.0
        var opacity: CGFloat = 1.0
        var swiped = false
    
        @IBOutlet weak var tempImageView: UIImageView!
        var tempImageViewtemp : UIImageView = UIImageView()
    
        // ---
        // --- this will hold a copy of the resized image (for reset)
        var resizedImage: UIImage!
    
        @IBOutlet weak var resetButton: UIButton!
        @IBOutlet weak var saveButton: UIButton!
    
        @IBAction func resetAct(_ sender: Any) {
            // ---
            // --- reset using the resized image from viewDidAppear
            tempImageView.image = resizedImage
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.tempImageView.image = UIImage(named: imageNameIs)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
    
            // ---
            // --- create a resized copy of the original image
            // --- (handles aspect fit scale)
            resizedImage = tempImageView.asImage()
            tempImageView.image = resizedImage
    
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            swiped = false
            if let touch = touches.first {
                // ---
                // --- get point ** in tempImageView **
                lastPoint = touch.location(in: self.tempImageView)  //view)
            }
        }
    
        func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
            // 1
    
            // ---
            // --- get the bounds of tempImageView
            let rect = tempImageView.bounds
    
            // ---
            // --- use the bounds for the image context size
            UIGraphicsBeginImageContext(rect.size)
    
            let context = UIGraphicsGetCurrentContext()
    
            // ---
            // --- draw the image into the bounds rect
            tempImageView.image?.draw(in: rect)
    
            // 3
            context!.move(to: fromPoint)
            context!.addLine(to: toPoint)
            context!.setLineCap(.round)
            context!.setLineWidth(brushWidth)
            context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
            context!.setBlendMode(.normal)
    
            // 4
            context!.strokePath()
            //CGContextStrokePath(context!)
    
            // 5
            tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
            tempImageView.alpha = opacity
            UIGraphicsEndImageContext()
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            swiped = true
            if let touch = touches.first {
    
                // ---
                // --- get point ** in tempImageView **
                let currentPoint = touch.location(in: self.tempImageView)
    
                drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
    
                // 7
                lastPoint = currentPoint
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            if !swiped {
                //draw a single point
                drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
            }
    
            // ---
            // don't know what you're doing with tempImageViewtemp,
            // but you probably want to change the sizing here to match elsewhere
            UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
            tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
            tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
            tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }
    }
    

    Edit:

    Here is a slightly different approach that may get you further on your way.

    Instead of changing your image, change the size of the imageView to match the ratio of the image.

    Storyboard layout:

    enter image description here

    The image view has constraints to remain centered in the white superview, and will be restricted to its width and height, maintaining proportion (aspect ratio).

    The white view has constraints keeping it sized / positioned above the buttons.

    Result - portrait orientation, with a circle drawn on left eye:

    enter image description here

    Result - device rotated to landscape orientation, with another circle drawn around the right eye:

    enter image description here

    You will also note in the code / comments that the "brushWidth" has to be adjusted based on the image height... using only 10-pts as the width will result in "thicker" lines when the image is shorter (landscape).

    Here is the code:

    //
    //  DrawOnViewController.swift
    //
    //  Created by Don Mag on 11/7/18.
    //
    
    import UIKit
    
    extension NSLayoutConstraint {
        /**
        Change multiplier constraint
    
        - parameter multiplier: CGFloat
        - returns: NSLayoutConstraint
        */
        func setMultiplier(multiplier:CGFloat) -> NSLayoutConstraint {
    
            NSLayoutConstraint.deactivate([self])
    
            let newConstraint = NSLayoutConstraint(
                item: firstItem as Any,
                attribute: firstAttribute,
                relatedBy: relation,
                toItem: secondItem,
                attribute: secondAttribute,
                multiplier: multiplier,
                constant: constant)
    
            newConstraint.priority = priority
            newConstraint.shouldBeArchived = self.shouldBeArchived
            newConstraint.identifier = self.identifier
    
            NSLayoutConstraint.activate([newConstraint])
            return newConstraint
        }
    }
    
    class DrawOnViewController: UIViewController {
    
        let imageNameIs = "BradPitImage"
        var lastPoint = CGPoint.zero
        var red: CGFloat = 0.0
        var green: CGFloat = 0.0
        var blue: CGFloat = 0.0
        var brushWidth: CGFloat = 10.0
        var opacity: CGFloat = 1.0
        var swiped = false
    
        @IBOutlet weak var tempImageView: UIImageView!
        var tempImageViewtemp : UIImageView = UIImageView()
    
        // ---
        // --- this will hold a reference to the image view's aspect ratio constraint
        @IBOutlet var imageViewAspectRatioConstraint: NSLayoutConstraint!
    
        // ---
        // --- this will be used to allow "brushWidth" to remain
        // --- constant when device is rotated
        var loadedImageHeight: CGFloat = 0.0
    
        // ---
        // --- this will be the "brushWidth" relative to
        // --- to the image's originial height
        var adjustedBrushWidth: CGFloat = 0.0
    
        @IBOutlet weak var resetButton: UIButton!
        @IBOutlet weak var saveButton: UIButton!
    
        @IBAction func resetAct(_ sender: Any) {
            if let img = UIImage(named: imageNameIs) {
                // ---
                // --- reset the imageView's image
                tempImageView.image = img
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let img = UIImage(named: imageNameIs) {
    
                let imgSize = img.size
    
                // ---
                // --- calculate the aspect ratio of the image
                let ratio = imgSize.width / imgSize.height
    
                // ---
                // --- change the image view's aspect ratio to match the image
                imageViewAspectRatioConstraint = imageViewAspectRatioConstraint.setMultiplier(multiplier: ratio)
    
                // ---
                // --- save original image height for brushWidth adjustment
                loadedImageHeight = imgSize.height
    
                // ---
                // --- set the imageView's image
                self.tempImageView.image = img
    
            }
    
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
    
            // ---
            // --- calculate brushWidth relative to image height scaled to image view height
            adjustedBrushWidth = brushWidth * loadedImageHeight / tempImageView.frame.height
        }
    
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            swiped = false
            if let touch = touches.first {
                // ---
                // --- get point ** in tempImageView **
                lastPoint = touch.location(in: self.tempImageView)
            }
        }
    
        func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
            // 1
    
            // ---
            // --- get the bounds of tempImageView
            let rect = tempImageView.bounds
    
            // ---
            // --- use the bounds for the image context size
            UIGraphicsBeginImageContext(rect.size)
    
            let context = UIGraphicsGetCurrentContext()
    
            // ---
            // --- draw the image into the bounds rect
            tempImageView.image?.draw(in: rect)
    
            // 3
            context!.move(to: fromPoint)
            context!.addLine(to: toPoint)
            context!.setLineCap(.round)
    
            // ---
            // --- set line width proportional to current imageView height
            // --- use adjustedBrushWidth set in viewDidAppear
    //      context!.setLineWidth(brushWidth * rect.height / loadedImageHeight)
            context!.setLineWidth(adjustedBrushWidth * rect.height / loadedImageHeight)
    
            context!.setStrokeColor(red: red, green: green, blue: blue, alpha: 1)
            context!.setBlendMode(.normal)
    
            // 4
            context!.strokePath()
            //CGContextStrokePath(context!)
    
            // 5
            tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
            tempImageView.alpha = opacity
            UIGraphicsEndImageContext()
        }
    
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            swiped = true
            if let touch = touches.first {
    
                // ---
                // --- get point ** in tempImageView **
                let currentPoint = touch.location(in: self.tempImageView)
    
                drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)
    
                // 7
                lastPoint = currentPoint
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
            if !swiped {
                //draw a single point
                drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
            }
    
            // ---
            // don't know what you're doing with tempImageViewtemp,
            // but you probably want to change the sizing here to match elsewhere
            //      UIGraphicsBeginImageContext(tempImageViewtemp.frame.size)
            //      tempImageViewtemp.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1)
            //      tempImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
            //      tempImageViewtemp.image = UIGraphicsGetImageFromCurrentImageContext()
            //      UIGraphicsEndImageContext()
        }
    }
    

    And here is the storyboard with the required constraints:

    <?xml version="1.0" encoding="UTF-8"?>
    <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bvb-cu-Gcn">
        <device id="retina4_7" orientation="portrait">
            <adaptation id="fullscreen"/>
        </device>
        <dependencies>
            <deployment identifier="iOS"/>
            <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
            <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
            <capability name="Safe area layout guides" minToolsVersion="9.0"/>
            <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
        </dependencies>
        <scenes>
            <!--Draw On View Controller-->
            <scene sceneID="b4T-Zn-uEk">
                <objects>
                    <viewController id="bvb-cu-Gcn" customClass="DrawOnViewController" customModule="SW4Temp" customModuleProvider="target" sceneMemberID="viewController">
                        <view key="view" contentMode="scaleToFill" id="fif-yb-Cf8">
                            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                            <subviews>
                                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GU5-HP-5fd">
                                    <rect key="frame" x="8" y="28" width="359" height="593"/>
                                    <subviews>
                                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="1MQ-UT-8dQ">
                                            <rect key="frame" x="0.5" y="117.5" width="358.5" height="358.5"/>
                                            <color key="backgroundColor" red="1" green="0.1857388616" blue="0.57339501380000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <constraints>
                                                <constraint firstAttribute="width" secondItem="1MQ-UT-8dQ" secondAttribute="height" multiplier="1:1" id="xD6-8c-1AI"/>
                                            </constraints>
                                        </imageView>
                                    </subviews>
                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                    <constraints>
                                        <constraint firstItem="1MQ-UT-8dQ" firstAttribute="height" secondItem="GU5-HP-5fd" secondAttribute="height" priority="750" id="14p-DB-cE1"/>
                                        <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="1MQ-UT-8dQ" secondAttribute="bottom" id="9A8-ey-EzV"/>
                                        <constraint firstItem="1MQ-UT-8dQ" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="GU5-HP-5fd" secondAttribute="leading" id="DbB-Mv-lEx"/>
                                        <constraint firstItem="1MQ-UT-8dQ" firstAttribute="top" relation="greaterThanOrEqual" secondItem="GU5-HP-5fd" secondAttribute="top" id="Eq0-pn-RKN"/>
                                        <constraint firstItem="1MQ-UT-8dQ" firstAttribute="centerY" secondItem="GU5-HP-5fd" secondAttribute="centerY" id="d9A-Un-06B"/>
                                        <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="1MQ-UT-8dQ" secondAttribute="trailing" id="dDp-HC-4pn"/>
                                        <constraint firstItem="1MQ-UT-8dQ" firstAttribute="centerX" secondItem="GU5-HP-5fd" secondAttribute="centerX" id="mqJ-4L-wQR"/>
                                    </constraints>
                                </view>
                                <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QmC-N4-hsp">
                                    <rect key="frame" x="20" y="629" width="39" height="30"/>
                                    <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    <state key="normal" title="Reset"/>
                                    <connections>
                                        <action selector="resetAct:" destination="bvb-cu-Gcn" eventType="touchUpInside" id="5ug-cY-v6A"/>
                                    </connections>
                                </button>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BWU-rw-ePF">
                                    <rect key="frame" x="321" y="629" width="34" height="30"/>
                                    <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    <state key="normal" title="Save"/>
                                    <connections>
                                        <action selector="resetAct:" destination="bvb-cu-Gcn" eventType="touchUpInside" id="JkQ-M7-brY"/>
                                    </connections>
                                </button>
                                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="sia-an-mGC">
                                    <rect key="frame" x="168.5" y="629" width="37" height="30"/>
                                    <color key="backgroundColor" red="0.99953407049999998" green="0.98835557699999999" blue="0.47265523669999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    <state key="normal" title="Color"/>
                                </button>
                            </subviews>
                            <color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                            <constraints>
                                <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="sia-an-mGC" secondAttribute="bottom" constant="8" id="33Q-9r-cik"/>
                                <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="BWU-rw-ePF" secondAttribute="bottom" constant="8" id="9Xw-wi-cSH"/>
                                <constraint firstItem="mdN-5j-gOT" firstAttribute="trailing" secondItem="BWU-rw-ePF" secondAttribute="trailing" constant="20" id="Bwp-Uu-Qt7"/>
                                <constraint firstItem="GU5-HP-5fd" firstAttribute="top" secondItem="mdN-5j-gOT" secondAttribute="top" constant="8" id="E63-DW-lHC"/>
                                <constraint firstItem="mdN-5j-gOT" firstAttribute="trailing" secondItem="GU5-HP-5fd" secondAttribute="trailing" constant="8" id="GDF-yb-Zhr"/>
                                <constraint firstItem="GU5-HP-5fd" firstAttribute="leading" secondItem="mdN-5j-gOT" secondAttribute="leading" constant="8" id="Pw9-SO-lkd"/>
                                <constraint firstItem="mdN-5j-gOT" firstAttribute="bottom" secondItem="QmC-N4-hsp" secondAttribute="bottom" constant="8" id="TKU-Ht-CmQ"/>
                                <constraint firstItem="QmC-N4-hsp" firstAttribute="leading" secondItem="mdN-5j-gOT" secondAttribute="leading" constant="20" id="Uwc-k4-TP4"/>
                                <constraint firstItem="QmC-N4-hsp" firstAttribute="top" secondItem="GU5-HP-5fd" secondAttribute="bottom" constant="8" id="ute-8t-Q2P"/>
                                <constraint firstItem="sia-an-mGC" firstAttribute="centerX" secondItem="fif-yb-Cf8" secondAttribute="centerX" id="zXd-nm-Goa"/>
                            </constraints>
                            <viewLayoutGuide key="safeArea" id="mdN-5j-gOT"/>
                        </view>
                        <connections>
                            <outlet property="imageViewAspectRatioConstraint" destination="xD6-8c-1AI" id="KWT-aA-gtN"/>
                            <outlet property="tempImageView" destination="1MQ-UT-8dQ" id="brX-iu-UgX"/>
                        </connections>
                    </viewController>
                    <placeholder placeholderIdentifier="IBFirstResponder" id="zoz-eV-W9h" userLabel="First Responder" sceneMemberID="firstResponder"/>
                </objects>
                <point key="canvasLocation" x="48.799999999999997" y="624.73763118440786"/>
            </scene>
        </scenes>
    </document>