diff --git a/src/Model/Except.py b/src/Model/Except.py
index a468b037ce03819596469e7b10d95a6289cc1ac5..4c71bbc29c577bf257e067830f15e9b805dc631f 100644
--- a/src/Model/Except.py
+++ b/src/Model/Except.py
@@ -116,3 +116,44 @@ class FileFormatError(ExeceptionWithMessageBox):
             _translate("Exception", "format because of") +
             f" '{self.reason}'"
         )
+
+
+class ClipboardFormatError(ExeceptionWithMessageBox):
+    def __init__(self, mime=None, header=None, data=None):
+        super(ClipboardFormatError, self).__init__(
+            title = _translate("Exception", "Clipboard format error")
+        )
+
+        self.mime = mime
+        self.header = header
+        self.data = data
+
+        if self.mime is not None:
+            self.msg = f"Impossible to decode data to mime code '{self.mime}'"
+        else:
+            if len(self.header) == 0:
+                msg = _translate("Exception", "without header")
+            else:
+                msg = (
+                    _translate("Exception", "with header") +
+                    f": {self.header}"
+                )
+
+                self.msg = (
+                    _translate("Exception", "Invalid clipboard data format:") +
+                    f" '{self.data}' {msg}"
+                )
+
+        self.alert()
+
+    def __str__(self):
+        return self.msg
+
+    def header(self):
+        return _translate("Exception", "Clipboard format error")
+
+    def short_message(self):
+        return _translate("Exception", "Clipboard format unknown")
+
+    def message(self):
+        return self.msg
diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py
index bd9dec94060ab9307027bb177fd1cd59653c5176..1f2b0dddb43b69ed017e96f4e09d4181d84a7e61 100644
--- a/src/Model/Geometry/ProfileXYZ.py
+++ b/src/Model/Geometry/ProfileXYZ.py
@@ -10,11 +10,12 @@ from Model.Geometry.PointXYZ import PointXYZ
 from Model.Geometry.Vector_1d import Vector1d
 
 class ProfileXYZ(Profile):
-    def __init__(self, num: int = 0,
-                 code1: int = 0, code2: int = 0,
+    def __init__(self,
+                 name: str = "",
+                 kp: float = 0.,
+                 reach = None,
                  nb_point: int = 0,
-                 kp: float = 0., name: str = "",
-                 reach = None):
+                 code1: int = 0, code2: int = 0):
         """ProfileXYZ constructor
 
         Args:
@@ -28,7 +29,7 @@ class ProfileXYZ(Profile):
             Nothing.
         """
         super(ProfileXYZ, self).__init__(
-            num = num,
+            num = 0,
             name = name,
             kp = kp,
             code1 = code1, code2 = code2,
@@ -36,6 +37,28 @@ class ProfileXYZ(Profile):
             reach = reach,
         )
 
+    @classmethod
+    def from_data(cls, header, data):
+        profile = None
+        try:
+            if len(header) == 0:
+                profile = cls(
+                    *data
+                )
+            else:
+                valid_header = {'name', 'reach', 'kp'}
+                d = {}
+                for i, v in enumerate(data):
+                    h = header[i].strip().lower().split(' ')[0]
+                    if h in valid_header:
+                        d[h] = v
+
+                profile = cls(**d)
+        except Exception as e:
+            raise ClipboardFormatError(header, data)
+
+        return profile
+
     def x(self):
         return [point.x for point in self._points]
 
diff --git a/src/View/ASubWindow.py b/src/View/ASubWindow.py
index a47a839a6b561674f4c07e8fc5250b3d0beffa10..89fd37b3d6c624bfb95116242a85b0bce55e4a55 100644
--- a/src/View/ASubWindow.py
+++ b/src/View/ASubWindow.py
@@ -24,14 +24,19 @@ class WindowToolKit(object):
     def __init__(self, parent=None):
         super(WindowToolKit, self).__init__()
 
+    def copyTableIntoClipboard(self, table):
+        stream = StringIO()
+        csv.writer(stream, delimiter='\t').writerows(table)
+        QApplication.clipboard().setText(stream.getvalue())
+
     def parseClipboardTable(self):
         clip = QApplication.clipboard()
         mime = clip.mimeData()
-        # print(mime.formats())
-        data = mime.data('text/plain').data().decode()
+        if 'text/plain' not in mime.formats():
+            raise ClipboardFormatError(mime='text/plain')
 
+        data = mime.data('text/plain').data().decode()
         has_header = csv.Sniffer().has_header(data)
-        print(f"header? {has_header}")
 
         header = []
         values = []
diff --git a/src/View/Geometry/GeometryWindow.py b/src/View/Geometry/GeometryWindow.py
index 3818b2b3fbb09711b32386c5eab50362ae4d68fa..3c162535f1871aa03a5bb8dcc2c571bbc503ef2f 100644
--- a/src/View/Geometry/GeometryWindow.py
+++ b/src/View/Geometry/GeometryWindow.py
@@ -6,7 +6,7 @@ import sys
 import time
 
 from copy import deepcopy
-from tools import timer
+from tools import timer, trace
 
 from PyQt5 import QtWidgets
 from PyQt5.QtGui import (
@@ -372,18 +372,27 @@ class GeometryWindow(QMainWindow, WindowToolKit):
                    .selectionModel()\
                    .selectedRows()
 
-        self._clipboard = []
+        table = []
+        table.append(["name", "kp"])
 
         for row in rows:
-            self._clipboard.append(
-                deepcopy(
-                    self._reach.profile(row.row())
-                )
+            profile = self._reach.profile(row.row())
+            table.append(
+                [profile.name, profile.kp]
             )
 
+        self.copyTableIntoClipboard(table)
+
     def paste(self):
+        header, data = self.parseClipboardTable()
+
+        if len(header) != 0:
+            header.append("reach")
+        for row in data:
+            row.append(self._reach)
+
         row = self.index_selected_row()
-        self._tablemodel.paste(row, self._clipboard)
+        self._tablemodel.paste(row, header, data)
         self.select_current_profile()
 
     def undo(self):
diff --git a/src/View/Geometry/qtableview_reach.py b/src/View/Geometry/qtableview_reach.py
index 56405933279e1c0102cc4a1094c48bd902cf4933..bf6d28ef396c2930580a865d72ca5c8a27b18ce7 100644
--- a/src/View/Geometry/qtableview_reach.py
+++ b/src/View/Geometry/qtableview_reach.py
@@ -2,6 +2,8 @@
 
 import time
 
+from tools import timer, trace
+
 from PyQt5.QtGui import (
     QKeySequence, QColor
 )
@@ -16,6 +18,7 @@ from PyQt5.QtWidgets import (
 )
 
 from Model.Geometry import Reach
+from Model.Geometry.ProfileXYZ import ProfileXYZ
 from View.Geometry.ReachUndoCommand import *
 
 
@@ -30,11 +33,16 @@ class TableEditableModel(QAbstractTableModel):
         self._undo_stack = undo
         self._reach = reach
 
+        # Hack for qtlinguist
+        _ = _translate("Geometry", "Name")
+        _ = _translate("Geometry", "Kp (m)")
+        _ = _translate("Geometry", "Type")
+
         if headers is None:
             self.headers = [
-                _translate("Geometry", "Name"),
-                _translate("Geometry", "Kp (m)"),
-                _translate("Geometry", "Type")
+                "Name",
+                "Kp (m)",
+                "Type"
             ]
         else:
             self.headers = headers
@@ -79,7 +87,7 @@ class TableEditableModel(QAbstractTableModel):
         if role == Qt.DisplayRole:
             if orientation == Qt.Horizontal:
                 if section < len(self.headers):
-                    return self.headers[section]
+                    return _translate("Geometry", self.headers[section])
             else:
                 return str(section + 1)
 
@@ -204,18 +212,25 @@ class TableEditableModel(QAbstractTableModel):
         self.endMoveRows()
         self.layoutChanged.emit()
 
-    def paste(self, row, profiles):
+    @trace
+    def paste(self, row, header, data):
         if row > self._reach.number_profiles:
             return
 
-        if len(profiles) == 0:
+        if len(data) == 0:
             return
 
         self.layoutAboutToBeChanged.emit()
 
         self._undo_stack.push(
             PasteCommand(
-                self._reach, row, profiles
+                self._reach, row,
+                list(
+                    map(
+                        lambda d: ProfileXYZ.from_data(header, d),
+                        data
+                    )
+                )
             )
         )