diff --git a/client/views/Exam.jsx b/client/views/Exam.jsx
index 3ccbc0087586e63a07fe50a09b7b2900dded0730..85737fb3b67934e7777f872f1bc5a1f0763100c5 100644
--- a/client/views/Exam.jsx
+++ b/client/views/Exam.jsx
@@ -42,11 +42,7 @@ class Exams extends React.Component {
             page: problem.page,
             name: problem.name,
             graded: problem.graded,
-            mc_options: problem.mc_options.map((option) => {
-              option.widget.x -= 7
-              option.widget.y -= 21
-              return option
-            }),
+            mc_options: problem.mc_options,
             isMCQ: problem.mc_options && problem.mc_options.length !== 0 // is the problem a mc question - used to display PanelMCQ
           }
         }
@@ -365,8 +361,8 @@ class Exams extends React.Component {
       'feedback_id': null,
       'widget': {
         'name': 'mc_option_' + labels[index],
-        'x': xPos + 7,
-        'y': yPos + 21,
+        'x': xPos,
+        'y': yPos,
         'type': 'mcq_widget'
       }
     }
@@ -379,8 +375,6 @@ class Exams extends React.Component {
     formData.append('label', data.label)
     api.put('mult-choice/', formData).then(result => {
       data.id = result.mult_choice_id
-      data.widget.x -= 7
-      data.widget.y -= 21
       this.createNewMCOWidget(problemWidget, data)
       this.generateAnswerBoxes(problemWidget, labels, index + 1, xPos + 24, yPos)
     }).catch(err => {
diff --git a/client/views/ExamEditor.jsx b/client/views/ExamEditor.jsx
index 311cadafb250703af69f983cae03a8e88438ea5e..15781e4c19b4aa826c3fb22667c62393a41cfeb9 100644
--- a/client/views/ExamEditor.jsx
+++ b/client/views/ExamEditor.jsx
@@ -187,27 +187,6 @@ class ExamEditor extends React.Component {
       this.props.updateExam()
     })
   }
-
-  updateState = (widget, data) => {
-    this.props.updateMCWidgetPosition(widget, {
-      x: Math.round(data.x),
-      y: Math.round(data.y)
-    })
-  }
-
-  updateMCOPosition = (widget, data) => {
-    this.updateState(widget, data)
-
-    widget.problem.mc_options.forEach(
-      (option, i) => {
-        let newData = {
-          x: Math.round(data.x) + i * 24 + 7,
-          y: Math.round(data.y) + 21
-        }
-        this.updateWidgetPositionDB(option, newData)
-      })
-  }
-
   /**
    * This function renders a group of options into one draggable widget
    * @returns {*}
@@ -217,9 +196,6 @@ class ExamEditor extends React.Component {
     let height = 38
     let enableResizing = false
     const isSelected = widget.id === this.props.selectedWidgetId
-    let xPos = widget.problem.mc_options[0].widget.x
-    let yPos = widget.problem.mc_options[0].widget.y
-
     return (
       <ResizeAndDrag
         key={'widget_mc_' + widget.id}
@@ -237,8 +213,8 @@ class ExamEditor extends React.Component {
           topRight: enableResizing
         }}
         position={{
-          x: xPos,
-          y: yPos
+          x: widget.problem.mc_options[0].widget.x,
+          y: widget.problem.mc_options[0].widget.y
         }}
         size={{
           width: width,
@@ -248,7 +224,19 @@ class ExamEditor extends React.Component {
           this.props.selectWidget(widget.id)
         }}
         onDragStop={(e, data) => {
-          this.updateMCOPosition(widget, data)
+          this.props.updateMCWidgetPosition(widget, {
+            x: Math.round(data.x),
+            y: Math.round(data.y)
+          })
+
+          widget.problem.mc_options.forEach(
+            (option, i) => {
+              let newData = {
+                x: Math.round(data.x),
+                y: Math.round(data.y) + i * 24
+              }
+              this.updateWidgetPositionDB(option, newData)
+            })
         }}
       >
         <div className={isSelected ? 'mcq-widget widget selected' : 'mcq-widget widget '}>
@@ -330,28 +318,6 @@ class ExamEditor extends React.Component {
         onDragStart={() => {
           this.props.selectWidget(widget.id)
         }}
-        onDrag={(e, data) => {
-          if (widget.problem.mc_options.length > 0) {
-            let xPos = widget.problem.mc_options[0].widget.x
-            let yPos = widget.problem.mc_options[0].widget.y
-            let width = 24 * widget.problem.mc_options.length
-            let height = 38
-
-            if (xPos < data.x) {
-              xPos = data.x
-            } else if (xPos + width > data.x + widget.width) {
-              xPos = data.x + widget.width - width
-            }
-
-            if (yPos < data.y) {
-              yPos = data.y
-            } else if (yPos + height > data.y + widget.height) {
-              yPos = data.y + widget.height - height
-            }
-
-            this.updateState(widget, { x: xPos, y: yPos })
-          }
-        }}
         onDragStop={(e, data) => {
           this.props.updateWidget(widget.id, {
             x: { $set: Math.round(data.x) },
@@ -361,26 +327,7 @@ class ExamEditor extends React.Component {
             x: Math.round(data.x),
             y: Math.round(data.y)
           }).then(() => {
-            if (widget.problem.mc_options.length > 0) {
-              let xPos = widget.problem.mc_options[0].widget.x
-              let yPos = widget.problem.mc_options[0].widget.y
-              let width = 24 * widget.problem.mc_options.length
-              let height = 38
-
-              if (xPos < data.x) {
-                xPos = data.x
-              } else if (xPos + width > data.x + widget.width) {
-                xPos = data.x + widget.width - width
-              }
-
-              if (yPos < data.y) {
-                yPos = data.y
-              } else if (yPos + height > data.y + widget.height) {
-                yPos = data.y + widget.height - height
-              }
-
-              this.updateMCOPosition(widget, { x: xPos, y: yPos })
-            }
+            // ok
           }).catch(err => {
             console.log(err)
             // update to try and get a consistent state
diff --git a/tests/data/cornermarkers/a4-3-markers.png b/tests/data/cornermarkers/a4-3-markers.png
deleted file mode 100644
index 32bdd11a2fbf87fed48625c3a12f0e609e2088a6..0000000000000000000000000000000000000000
Binary files a/tests/data/cornermarkers/a4-3-markers.png and /dev/null differ
diff --git a/tests/data/cornermarkers/a4-rotated-3-markers.png b/tests/data/cornermarkers/a4-rotated-3-markers.png
deleted file mode 100644
index d32cbacac4d4a2d8a327736ce8967623f8aca22d..0000000000000000000000000000000000000000
Binary files a/tests/data/cornermarkers/a4-rotated-3-markers.png and /dev/null differ
diff --git a/tests/data/cornermarkers/a4-rotated.png b/tests/data/cornermarkers/a4-rotated.png
deleted file mode 100644
index 8dbf8630a73b83a71dcc36eb687933632657f5aa..0000000000000000000000000000000000000000
Binary files a/tests/data/cornermarkers/a4-rotated.png and /dev/null differ
diff --git a/tests/test_three_corners.py b/tests/test_three_corners.py
deleted file mode 100644
index 1169be12790279fffebab2502cbea72e01f7117f..0000000000000000000000000000000000000000
--- a/tests/test_three_corners.py
+++ /dev/null
@@ -1,57 +0,0 @@
-import cv2
-import os
-import numpy as np
-
-from zesje.images import fix_corner_markers
-from zesje.scans import find_corner_marker_keypoints
-
-
-def test_three_straight_corners_1():
-    shape = (240, 200, 3)
-    corner_markers = [(50, 50), (120, 50), (50, 200)]
-
-    top_left, corner_markers = fix_corner_markers(corner_markers, shape)
-
-    assert (120, 200) in corner_markers
-    assert top_left == (50, 50)
-
-
-def test_three_straight_corners_2():
-    shape = (240, 200, 3)
-    corner_markers = [(120, 50), (50, 200), (120, 200)]
-
-    top_left, corner_markers = fix_corner_markers(corner_markers, shape)
-
-    assert (50, 50) in corner_markers
-    assert top_left == (50, 50)
-
-
-def test_pdf(datadir):
-    # Max deviation of inferred corner marker and actual location
-    epsilon = 2
-
-    # Scan rotated image with 4 corner markers
-    image_filename1 = 'a4-rotated.png'
-    image_path = os.path.join(datadir, 'cornermarkers', image_filename1)
-    page_img = cv2.imread(image_path)
-
-    corners1 = find_corner_marker_keypoints(page_img)
-
-    # Scan the same image with 3 corner markers
-    image_filename2 = 'a4-rotated-3-markers.png'
-    image_path = os.path.join(datadir, 'cornermarkers', image_filename2)
-    page_img = cv2.imread(image_path)
-
-    corners2 = find_corner_marker_keypoints(page_img)
-
-    # Get marker that was removed
-    diff = [corner for corner in corners1 if corner not in corners2]
-    diff_marker = min(diff)
-
-    _, fixed_corners2 = fix_corner_markers(corners2, page_img.shape)
-    added_marker = [corner for corner in fixed_corners2 if corner not in corners2][0]
-
-    # Check if 'inferred' corner marker is not too far away
-    dist = np.linalg.norm(np.subtract(added_marker, diff_marker))
-
-    assert dist < epsilon
diff --git a/zesje/api/exams.py b/zesje/api/exams.py
index a1c449a05bd1ad5d5a1f50a75c7e3698d66114e5..c88db56497c2e223b93a3a6ce9b2e9574b0e72f1 100644
--- a/zesje/api/exams.py
+++ b/zesje/api/exams.py
@@ -47,7 +47,8 @@ def get_cb_data_for_exam(exam):
     cb_data = []
     for problem in exam.problems:
         page = problem.widget.page
-        cb_data += [(cb.x, cb.y, page, cb.label) for cb in problem.mc_options]
+        if page:
+            cb_data += [(cb.x, cb.y, page, cb.label) for cb in problem.mc_options]
 
     return cb_data
 
diff --git a/zesje/api/mult_choice.py b/zesje/api/mult_choice.py
index 29eaa61dfa5363f18a0a7afb91d78893da1a678d..8a1b2c9ffd72ba1445b18a029efd6c62503626b8 100644
--- a/zesje/api/mult_choice.py
+++ b/zesje/api/mult_choice.py
@@ -64,8 +64,8 @@ class MultipleChoice(Resource):
         mc_type = 'mcq_widget'
 
         if not id:
-            # Insert new empty feedback option that links to the same problem, with the label as name
-            new_feedback_option = FeedbackOption(problem_id=problem_id, text=label)
+            # Insert new empty feedback option that links to the same problem
+            new_feedback_option = FeedbackOption(problem_id=problem_id, text='')
             db.session.add(new_feedback_option)
             db.session.commit()
 
diff --git a/zesje/images.py b/zesje/images.py
index 331a7a087864bf90d6d99f4e34aafb8110f75d34..0e2167f278b7d4e9e407d317c16cbd7eed00ee45 100644
--- a/zesje/images.py
+++ b/zesje/images.py
@@ -2,8 +2,6 @@
 
 import numpy as np
 
-from operator import sub, add
-
 
 def guess_dpi(image_array):
     h, *_ = image_array.shape
@@ -38,63 +36,6 @@ def get_box(image_array, box, padding=0.3):
     return image_array[top:bottom, left:right]
 
 
-def fix_corner_markers(corner_keypoints, shape):
-    """
-    Corrects the list of corner markers if only three corner markers are found.
-    This function raises if less than three corner markers are detected.
-
-    Parameters
-    ----------
-    corner_keypoints :
-        List of corner marker locations as tuples
-    shape :
-        Shape of the image in (x, y, dim)
-
-    Returns
-    -------
-    corner_keypoints :
-        A list of four corner markers.
-    top_left : tuple
-        Coordinates of the top left corner marker
-    """
-
-    if len(corner_keypoints) == 4 or len(corner_keypoints) < 3:
-        raise RuntimeError("Fewer then 3 corner markers found")
-
-    x_sep = shape[1] / 2
-    y_sep = shape[0] / 2
-
-    top_left = [(x, y) for x, y in corner_keypoints if x < x_sep and y < y_sep]
-    bottom_left = [(x, y) for x, y in corner_keypoints if x < x_sep and y > y_sep]
-    top_right = [(x, y) for x, y in corner_keypoints if x > x_sep and y < y_sep]
-    bottom_right = [(x, y) for x, y in corner_keypoints if x > x_sep and y > y_sep]
-
-    missing_point = ()
-
-    if not top_left:
-        # Top left point is missing
-        (dx, dy) = tuple(map(sub, top_right[0], bottom_right[0]))
-        missing_point = tuple(map(add, bottom_left[0], (dx, dy)))
-        top_left = [missing_point]
-
-    elif not bottom_left:
-        # Bottom left point is missing
-        (dx, dy) = tuple(map(sub, top_right[0], bottom_right[0]))
-        missing_point = tuple(map(sub, top_left[0], (dx, dy)))
-
-    elif not top_right:
-        # Top right point is missing
-        (dx, dy) = tuple(map(sub, top_left[0], bottom_left[0]))
-        missing_point = tuple(map(add, bottom_right[0], (dx, dy)))
-
-    elif not bottom_right:
-        # bottom right
-        (dx, dy) = tuple(map(sub, top_left[0], bottom_left[0]))
-        missing_point = tuple(map(sub, top_right[0], (dx, dy)))
-
-    return top_left[0], corner_keypoints + [missing_point]
-
-
 def box_is_filled(image_array, box_coords, padding=0.3, threshold=150, pixels=False):
     """
     Determines if a box is filled
diff --git a/zesje/pdf_generation.py b/zesje/pdf_generation.py
index 09bf9641bd13e631eb02a163f69ef2959b7955d6..0c0b8c5ae143e7ff7004adf57aaf353fc96ad565 100644
--- a/zesje/pdf_generation.py
+++ b/zesje/pdf_generation.py
@@ -162,27 +162,25 @@ def generate_checkbox(canvas, x, y, label):
     canvas : reportlab canvas object
 
     x : int
-        the x coordinate of the top left corner of the box in points (pt)
+        the x coordinate of the top left corner of the box in pixels
     y : int
-        the y coordinate of the top left corner of the box in points (pt)
+        the y coordinate of the top left corner of the box in pixels
     label: str
         A string representing the label that is drawn on top of the box, will only take the first character
 
     """
     fontsize = 11  # Size of font
     margin = 5  # Margin between elements and sides
-    markboxsize = fontsize - 2  # Size of checkboxes boxes
-    x_label = x + 1  # location of the label
-    y_label = y + margin  # remove fontsize from the y label since we draw from the bottom left up
-    box_y = y - markboxsize  # remove the markboxsize because the y is the coord of the top
-    # and reportlab prints from the bottom
+    markboxsize = fontsize - 2  # Size of student number boxes
+    x_label = x + 1
+    y_label = y + margin + fontsize
 
     # check that there is a label to print
     if (label and not (len(label) == 0)):
         canvas.setFont('Helvetica', fontsize)
         canvas.drawString(x_label, y_label, label[0])
 
-    canvas.rect(x, box_y, markboxsize, markboxsize)
+    canvas.rect(x, y, markboxsize, markboxsize)
 
 
 def generate_datamatrix(exam_id, page_num, copy_num):
@@ -275,8 +273,6 @@ def _generate_overlay(canv, pagesize, exam_id, copy_num, num_pages, id_grid_x,
         index = 0
         max_index = len(cb_data)
         cb_data = sorted(cb_data, key=lambda tup: tup[2])
-        # invert the y axis
-        cb_data = [(cb[0], pagesize[1] - cb[1], cb[2], cb[3]) for cb in cb_data]
     else:
         index = 0
         max_index = 0
diff --git a/zesje/pregrader.py b/zesje/pregrader.py
deleted file mode 100644
index 4954bc8a7d2083f782ce0c03e42c74284e2e12f7..0000000000000000000000000000000000000000
--- a/zesje/pregrader.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import cv2
-import numpy as np
-
-from zesje.database import db, Solution
-from zesje.images import guess_dpi, get_box, fix_corner_markers
-
-
-def add_feedback_to_solution(sub, exam, page, page_img, corner_keypoints):
-    """
-    Adds the multiple choice options that are identified as marked as a feedback option to a solution
-
-    Parameters
-    ------
-    sub : Submission
-        the current submission
-    exam : Exam
-        the current exam
-    page_img : Image
-        image of the page
-    corner_keypoints : array
-        locations of the corner keypoints as (x, y) tuples
-    """
-    problems_on_page = [problem for problem in exam.problems if problem.widget.page == page]
-
-    top_left_point, fixed_corner_keypoints = fix_corner_markers(corner_keypoints, page_img.shape)
-
-    for problem in problems_on_page:
-        sol = Solution.query.filter(Solution.problem_id == problem.id, Solution.submission_id == sub.id).one_or_none()
-
-        for mc_option in problem.mc_options:
-            box = (mc_option.x, mc_option.y)
-
-            if box_is_filled(box, page_img, top_left_point):
-                feedback = mc_option.feedback
-                sol.feedback.append(feedback)
-                db.session.commit()
-
-
-def box_is_filled(box, page_img, corner_keypoints, marker_margin=72/2.54, threshold=225, cut_padding=0.1, box_size=11):
-    """
-    A function that finds the checkbox in a general area and then checks if it is filled in.
-
-    Params
-    ------
-    box: (int, int)
-        The coordinates of the top left (x,y) of the checkbox in points.
-    page_img: np.array
-        A numpy array of the image scan
-    corner_keypoints: (float,float)
-        The x coordinate of the left markers and the y coordinate of the top markers,
-        used as point of reference since scans can deviate from the original.
-        (x,y) are both in pixels.
-    marker_margin: float
-        The margin between the corner markers and the edge of a page when generated.
-    threshold: int
-        the threshold needed for a checkbox to be considered marked range is between 0 (fully black)
-        and 255 (absolutely white).
-    cut_padding: float
-        The extra padding when retrieving an area where the checkbox is in inches.
-    box_size: int
-        the size of the checkbox in points.
-
-    Output
-    ------
-    True if the box is marked, else False.
-    """
-
-    # shouldn't be needed, but some images are drawn a bit weirdly
-    y_shift = 5
-    # create an array with y top, y bottom, x left and x right. use the marker margin to allign to the page.
-    coords = np.asarray([box[1] - marker_margin + y_shift, box[1] + box_size - marker_margin + y_shift,
-                        box[0] - marker_margin, box[0] + box_size - marker_margin])/72
-
-    # add the actually margin from the scan to corner markers to the coords in inches
-    dpi = guess_dpi(page_img)
-    coords[0] = coords[0] + corner_keypoints[1]/dpi
-    coords[1] = coords[1] + corner_keypoints[1]/dpi
-    coords[2] = coords[2] + corner_keypoints[0]/dpi
-    coords[3] = coords[3] + corner_keypoints[0]/dpi
-
-    # get the box where we think the box is
-    cut_im = get_box(page_img, coords, padding=cut_padding)
-
-    # convert to grayscale
-    gray_im = cv2.cvtColor(cut_im, cv2.COLOR_BGR2GRAY)
-    # apply threshold to only have black or white
-    _, bin_im = cv2.threshold(gray_im, 150, 255, cv2.THRESH_BINARY)
-
-    h_bin, w_bin, *_ = bin_im.shape
-    # create a mask that gets applied when floodfill the white
-    mask = np.zeros((h_bin+2, w_bin+2), np.uint8)
-    flood_im = bin_im.copy()
-    # fill the image from the top left
-    cv2.floodFill(flood_im, mask, (0, 0),  0)
-    # fill it from the bottom right just in case the top left doesn't cover all the white
-    cv2.floodFill(flood_im, mask, (h_bin-2, w_bin-2), 0)
-
-    # find white parts
-    coords = cv2.findNonZero(flood_im)
-    # Find a bounding box of the white parts
-    x, y, w, h = cv2.boundingRect(coords)
-    # cut the image to this box
-    res_rect = bin_im[y:y+h, x:x+w]
-
-    # the size in pixels we expect the drawn box to
-    box_size_px = box_size*dpi / 72
-
-    # if the rectangle is bigger (higher) than expected, cut the image up a bit
-    if h > 1.5 * box_size_px:
-        print("in h resize")
-        y_partition = 0.333
-        # try getting another bounding box on bottom 2/3 of the screen
-        coords2 = cv2.findNonZero(flood_im[y + int(y_partition * h): y + h, x: x+w])
-        x2, y2, w2, h2 = cv2.boundingRect(coords2)
-        # add these coords to create a new bounding box we are looking at
-        new_y = y+y2 + int(y_partition * h)
-        new_x = x + x2
-        res_rect = bin_im[new_y:new_y + h2, new_x:new_x + w2]
-
-    else:
-        new_x, new_y, w2, h2 = x, y, w, h
-
-    # do the same for width
-    if w2 > 1.5 * box_size_px:
-        # usually the checkbox is somewhere in the bottom left of the bounding box
-        coords3 = cv2.findNonZero(flood_im[new_y: new_y + h2, new_x: new_x + int(0.66 * w2)])
-        x3, y3, w3, h3 = cv2.boundingRect(coords3)
-        res_rect = bin_im[new_y + y3: new_y + y3 + h3, new_x + x3: new_x + x3 + w3]
-
-    # if the found box is smaller than a certain threshold
-    # it means that we only found a little bit of white and the box is filled
-    res_x, res_y, *_ = res_rect.shape
-    if res_x < 0.333 * box_size_px or res_y < 0.333 * box_size_px:
-        return True
-    return np.average(res_rect) < threshold
diff --git a/zesje/scans.py b/zesje/scans.py
index 4d3e1c5b963ef2b3432ad95c8a62c712e550d2ac..bcbde9f68e30f93eea06f2403cc5d0a35844f6ce 100644
--- a/zesje/scans.py
+++ b/zesje/scans.py
@@ -5,7 +5,6 @@ import os
 from collections import namedtuple, Counter
 from io import BytesIO
 import signal
-import traceback
 
 import cv2
 import numpy as np
@@ -18,7 +17,7 @@ from .database import db, Scan, Exam, Page, Student, Submission, Solution, ExamW
 from .datamatrix import decode_raw_datamatrix
 from .images import guess_dpi, get_box
 from .factory import make_celery
-from .pregrader import add_feedback_to_solution
+
 
 ExtractedBarcode = namedtuple('ExtractedBarcode', ['token', 'copy', 'page'])
 
@@ -55,7 +54,7 @@ def process_pdf(scan_id):
         # TODO: When #182 is implemented, properly separate user-facing
         #       messages (written to DB) from developer-facing messages,
         #       which should be written into the log.
-        write_pdf_status(scan_id, 'error', f"Unexpected error: {error}\n Traceback:\n" + traceback.format_exc())
+        write_pdf_status(scan_id, 'error', "Unexpected error: " + str(error))
 
 
 def _process_pdf(scan_id, app_config):
@@ -92,8 +91,8 @@ def _process_pdf(scan_id, app_config):
                     print(description)
                     failures.append(page)
             except Exception as e:
-                report_error(f'Error processing page {e}.\nTraceback:\n{traceback.format_exc()}')
-                raise
+                report_error(f'Error processing page {page}: {e}')
+                return
     except Exception as e:
         report_error(f"Failed to read pdf: {e}")
         raise
@@ -338,13 +337,7 @@ def process_page(image_data, exam_config, output_dir=None, strict=False):
     else:
         return True, "Testing, image not saved and database not updated."
 
-    sub, exam = update_database(image_path, barcode)
-
-    try:
-        add_feedback_to_solution(sub, exam, barcode.page, image_array, corner_keypoints)
-    except RuntimeError as e:
-        if strict:
-            return False, str(e)
+    update_database(image_path, barcode)
 
     if barcode.page == 0:
         description = guess_student(
@@ -392,12 +385,8 @@ def update_database(image_path, barcode):
 
     Returns
     -------
-    sub, exam where
-
-    sub : Submission
-        the current submission
-    exam : Exam
-        the current exam
+    signature_validated : bool
+        If the corresponding submission has a validated signature.
     """
     exam = Exam.query.filter(Exam.token == barcode.token).first()
     if exam is None:
@@ -417,8 +406,6 @@ def update_database(image_path, barcode):
 
     db.session.commit()
 
-    return sub, exam
-
 
 def decode_barcode(image, exam_config):
     """Extract a barcode from a PIL Image."""