diff --git a/db/install.xml b/db/install.xml
old mode 100644
new mode 100755
index 3324d944bee9ea463f3a3d9d064af096383b7442..102667decb1d77bd9bc68cd3ef3ff5d6ae7135a8
--- a/db/install.xml
+++ b/db/install.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/pdfannotator/db" VERSION="20181017" COMMENT="XMLDB file for Moodle mod/pdfannotator"
+<XMLDB PATH="mod/pdfannotator/db" VERSION="20221101" COMMENT="XMLDB file for Moodle mod/pdfannotator"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
@@ -48,9 +48,7 @@
         <FIELD NAME="isquestion" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="When the user creates an annotation, the comment he has to write is a question. So this column has to be true, otherwise false."/>
         <FIELD NAME="isdeleted" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="ishidden" TYPE="int" LENGTH="2" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Hidden comments can be seen by managers but not participants."/>
-        <FIELD NAME="solved" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="saves the userid of the user who marked the comment
-question: marked as solved
-answer: marked as correct answer"/>
+        <FIELD NAME="solved" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="saves the userid of the user who marked the comment question: marked as solved answer: marked as correct answer"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
diff --git a/db/upgrade.php b/db/upgrade.php
old mode 100644
new mode 100755
index c623f1f6262450d4554bac0639c9431a9bb58da8..d831d7a6ebfc3b41ffeab592ee9869ed44d1ee4c
--- a/db/upgrade.php
+++ b/db/upgrade.php
@@ -603,5 +603,46 @@ function xmldb_pdfannotator_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2021032201, 'pdfannotator');
     }
 
+    if ($oldversion < 2022102606) {
+
+        // Define table pdfannotator_embeddedfiles to be created.
+        $table = new xmldb_table('pdfannotator_embeddedfiles');
+
+        // Adding fields to table pdfannotator_embeddedfiles.
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('fileid', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('commentid', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null);
+
+        // Adding keys to table pdfannotator_embeddedfiles.
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
+        $table->add_key('fileid', XMLDB_KEY_FOREIGN, ['fileid'], 'files', ['id']);
+        $table->add_key('commentid', XMLDB_KEY_FOREIGN, ['commentid'], 'comments', ['id']);
+
+        // Adding indexes to table pdfannotator_embeddedfiles.
+        $table->add_index('idandcomment', XMLDB_INDEX_NOTUNIQUE, ['id', 'commentid']);
+
+        // Conditionally launch create table for pdfannotator_embeddedfiles.
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+        // Pdfannotator savepoint reached.
+        upgrade_mod_savepoint(true, 2022102606, 'pdfannotator');
+    }
+
+    if ($oldversion < 2022110200) {
+
+        // Define table pdfannotator_embeddedfiles to be dropped.
+        $table = new xmldb_table('pdfannotator_embeddedfiles');
+
+        // Conditionally launch drop table for pdfannotator_embeddedfiles.
+        if ($dbman->table_exists($table)) {
+            $dbman->drop_table($table);
+        }
+
+        // Pdfannotator savepoint reached.
+        upgrade_mod_savepoint(true, 2022110200, 'pdfannotator');
+    }
+
     return true;
 }
diff --git a/lang/en/pdfannotator.php b/lang/en/pdfannotator.php
index 77a3609504c93366dea4af44c9bd6ef04d71eae1..b27d831edc5cb01919dd73bc2d6c6b3d3960dffc 100644
--- a/lang/en/pdfannotator.php
+++ b/lang/en/pdfannotator.php
@@ -113,8 +113,7 @@ $string['error:hideComment'] = "An error has occured while trying to hide the co
 $string['error:markasread'] = 'The item could not be marked as read.';
 $string['error:markasunread'] = 'The item could not be marked as unread.';
 $string['error:markcorrectanswer'] = 'An error has occured while marking the answer as correct.';
-$string['error:maximalsizeoffile_created'] = 'Your comment cannot be created, because it exceeds the maximum size of files. You can attach file(s) with at most {$a} to a single comment.';
-$string['error:maximalsizeoffile_edited'] = 'Your comment cannot be edited, because it exceeds the maximum size of files. You can attach file(s) with at most {$a} to a single comment.';
+$string['error:maximalsizeoffile'] = 'Your file {$a->filename}, because it exceeds {$a->filesize} as the maximum size of files. You can attach file(s) with at most {$a->maxfilesize} to a single comment.';
 $string['error:missingAnnotationtype'] = 'Annotationtype does not exists. Possibly the entry in table pdfannotator_annotationtypes is missing.';
 $string['error:openingPDF'] = 'An error occurred while opening the PDF file.';
 $string['error:openprintview'] = 'An error has occured while trying to open the pdf in Acrobat Reader.';
diff --git a/locallib.php b/locallib.php
index 48ec13f2c669af80e0cb5f1337583f8d7661065e..8210ee36f68244e5c55b1ae203719762fb74de03 100644
--- a/locallib.php
+++ b/locallib.php
@@ -211,20 +211,22 @@ function pdfannotator_split_content_image($content, $res, $itemid, $context=null
 
         $tempinfo = [];
         foreach($fileinfo as $file) {
-            $count = substr_count($imgstr, $file['filename']);
+            $count = substr_count(urldecode($url[0]), $file['filename']);
             if($count) {
                 $tempinfo = $file;
                 break;
             }
         }
 
-        $imagedata = 'data:' . $tempinfo['filemimetype'] . ';base64,' .  base64_encode($tempinfo['filecontent']);
-        $data['image'] = $imagedata;
-        $data['format'] = $tempinfo['filemimetype'];
-        $data['fileid'] = $tempinfo['fileid'];
-        $data['filename'] = $tempinfo['filename'];
-        $data['filepath'] = $tempinfo['filepath'];
-        $data['filesize'] = $tempinfo['filesize'];
+        if($tempinfo) {
+            $imagedata = 'data:' . $tempinfo['filemimetype'] . ';base64,' .  base64_encode($tempinfo['filecontent']);
+            $data['image'] = $imagedata;
+            $data['format'] = $tempinfo['filemimetype'];
+            $data['fileid'] = $tempinfo['fileid'];
+            $data['filename'] = $tempinfo['filename'];
+            $data['filepath'] = $tempinfo['filepath'];
+            $data['filesize'] = $tempinfo['filesize'];
+        }
 
         preg_match('/height=[0-9]+/', $imgstr, $height);
         $data['imageheight'] = str_replace("\"", "", explode('=', $height[0])[1]);
@@ -263,7 +265,7 @@ function pdfannotator_data_preprocessing($context, $textarea, $draftitemid = 0)
     if(!$imagebtn) {
         $editor->use_editor($textarea, $options);
     } else {
-        // inilialize Filepicker if image button is active.
+        // initialize Filepicker if image button is active.
         $args = new \stdClass();    
         // need these three to filter repositories list.    
         $args->accepted_types = ['web_image'];
@@ -295,7 +297,7 @@ function pdfannotator_data_preprocessing($context, $textarea, $draftitemid = 0)
 
 /**
  * Same function as core, however we need to add files into the existing draft area!
- * 
+ * Copied from hsuforum.
  */
 function pdfannotator_file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
     global $CFG, $USER, $CFG, $DB;
@@ -366,6 +368,233 @@ function pdfannotator_file_prepare_draft_area(&$draftitemid, $contextid, $compon
     return file_rewrite_pluginfile_urls($text, 'draftfile.php', $usercontext->id, 'user', 'draft', $draftitemid, $options);
 }
 
+/**
+ * Just like the file_save_draft_area_files core function.
+ * However, we need to store fileid and commentid in pdfannotator_embeddedfiles
+ * and add its id into the html code from the corresponding files.
+ * @return string|null if $text was passed in, the rewritten $text is returned. 
+ * Otherwise NULL.
+ */
+function pdfannotator_file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null, $forcehttps=false) {
+    global $USER, $DB;
+
+    // Do not merge files, leave it as it was.
+    if ($draftitemid === IGNORE_FILE_MERGE) {
+        // Safely return $text, no need to rewrite pluginfile because this is mostly comming from an external client like the app.
+        return $text;
+    }
+
+    $usercontext = context_user::instance($USER->id);
+    $fs = get_file_storage();
+
+    $options = (array)$options;
+    if (!isset($options['subdirs'])) {
+        $options['subdirs'] = false;
+    }
+    if (!isset($options['maxfiles'])) {
+        $options['maxfiles'] = -1; // unlimited
+    }
+    if (!isset($options['maxbytes']) || $options['maxbytes'] == USER_CAN_IGNORE_FILE_SIZE_LIMITS) {
+        $options['maxbytes'] = 0; // unlimited
+    }
+    if (!isset($options['areamaxbytes'])) {
+        $options['areamaxbytes'] = FILE_AREA_MAX_BYTES_UNLIMITED; // Unlimited.
+    }
+    $allowreferences = true;
+    if (isset($options['return_types']) && !($options['return_types'] & (FILE_REFERENCE | FILE_CONTROLLED_LINK))) {
+        // we assume that if $options['return_types'] is NOT specified, we DO allow references.
+        // this is not exactly right. BUT there are many places in code where filemanager options
+        // are not passed to file_save_draft_area_files()
+        $allowreferences = false;
+    }
+
+    // Check if the user has copy-pasted from other draft areas. Those files will be located in different draft
+    // areas and need to be copied into the current draft area.
+    $text = file_merge_draft_areas($draftitemid, $usercontext->id, $text, $forcehttps);
+
+    // Check if the draft area has exceeded the authorised limit. This should never happen as validation
+    // should have taken place before, unless the user is doing something nauthly. If so, let's just not save
+    // anything at all in the next area.
+    if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) {
+        return null;
+    }
+
+    $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id');
+    $oldfiles   = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id');
+
+    // One file in filearea means it is empty (it has only top-level directory '.').
+    if (count($draftfiles) > 1 || count($oldfiles) > 1) {
+        // we have to merge old and new files - we want to keep file ids for files that were not changed
+        // we change time modified for all new and changed files, we keep time created as is
+
+        $newhashes = array();
+        $filecount = 0;
+        $context = context::instance_by_id($contextid, MUST_EXIST);
+        foreach ($draftfiles as $file) {
+            if (!$options['subdirs'] && $file->get_filepath() !== '/') {
+                continue;
+            }
+            if (!$allowreferences && $file->is_external_file()) {
+                continue;
+            }
+            if (!$file->is_directory()) {
+                // Check to see if this file was uploaded by someone who can ignore the file size limits.
+                $fileusermaxbytes = get_user_max_upload_file_size($context, $options['maxbytes'], 0, 0, $file->get_userid());
+                if ($fileusermaxbytes != USER_CAN_IGNORE_FILE_SIZE_LIMITS
+                        && ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize())) {
+                    // Oversized file.
+                    continue;
+                }
+                if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
+                    // more files - should not get here at all
+                    continue;
+                }
+                $filecount++;
+            }
+            $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename());
+            $newhashes[$newhash] = $file;
+        }
+
+        // Loop through oldfiles and decide which we need to delete and which to update.
+        // After this cycle the array $newhashes will only contain the files that need to be added.
+        foreach ($oldfiles as $oldfile) {
+            $oldhash = $oldfile->get_pathnamehash();
+            if (!isset($newhashes[$oldhash])) {
+                // delete files not needed any more - deleted by user
+                $oldfile->delete();
+                continue;
+            }
+
+            $newfile = $newhashes[$oldhash];
+            // Now we know that we have $oldfile and $newfile for the same path.
+            // Let's check if we can update this file or we need to delete and create.
+            if ($newfile->is_directory()) {
+                // Directories are always ok to just update.
+            } else if (($source = @unserialize($newfile->get_source())) && isset($source->original)) {
+                // File has the 'original' - we need to update the file (it may even have not been changed at all).
+                $original = file_storage::unpack_reference($source->original);
+                if ($original['filename'] !== $oldfile->get_filename() || $original['filepath'] !== $oldfile->get_filepath()) {
+                    // Very odd, original points to another file. Delete and create file.
+                    $oldfile->delete();
+                    continue;
+                }
+            } else {
+                // The same file name but absence of 'original' means that file was deteled and uploaded again.
+                // By deleting and creating new file we properly manage all existing references.
+                $oldfile->delete();
+                continue;
+            }
+
+            // status changed, we delete old file, and create a new one
+            if ($oldfile->get_status() != $newfile->get_status()) {
+                // file was changed, use updated with new timemodified data
+                $oldfile->delete();
+                // This file will be added later
+                continue;
+            }
+
+            // Updated author
+            if ($oldfile->get_author() != $newfile->get_author()) {
+                $oldfile->set_author($newfile->get_author());
+            }
+            // Updated license
+            if ($oldfile->get_license() != $newfile->get_license()) {
+                $oldfile->set_license($newfile->get_license());
+            }
+
+            // Updated file source
+            // Field files.source for draftarea files contains serialised object with source and original information.
+            // We only store the source part of it for non-draft file area.
+            $newsource = $newfile->get_source();
+            if ($source = @unserialize($newfile->get_source())) {
+                $newsource = $source->source;
+            }
+            if ($oldfile->get_source() !== $newsource) {
+                $oldfile->set_source($newsource);
+            }
+
+            // Updated sort order
+            if ($oldfile->get_sortorder() != $newfile->get_sortorder()) {
+                $oldfile->set_sortorder($newfile->get_sortorder());
+            }
+
+            // Update file timemodified
+            if ($oldfile->get_timemodified() != $newfile->get_timemodified()) {
+                $oldfile->set_timemodified($newfile->get_timemodified());
+            }
+
+            // Replaced file content
+            if (!$oldfile->is_directory() &&
+                    ($oldfile->get_contenthash() != $newfile->get_contenthash() ||
+                    $oldfile->get_filesize() != $newfile->get_filesize() ||
+                    $oldfile->get_referencefileid() != $newfile->get_referencefileid() ||
+                    $oldfile->get_userid() != $newfile->get_userid())) {
+                $oldfile->replace_file_with($newfile);
+            }
+
+            // unchanged file or directory - we keep it as is
+            unset($newhashes[$oldhash]);
+        }
+
+        // Add fresh file or the file which has changed status
+        // the size and subdirectory tests are extra safety only, the UI should prevent it
+        foreach ($newhashes as $file) {
+            $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
+            if ($source = @unserialize($file->get_source())) {
+                // Field files.source for draftarea files contains serialised object with source and original information.
+                // We only store the source part of it for non-draft file area.
+                $file_record['source'] = $source->source;
+            }
+
+            if ($file->is_external_file()) {
+                $repoid = $file->get_repository_id();
+                if (!empty($repoid)) {
+                    $context = context::instance_by_id($contextid, MUST_EXIST);
+                    $repo = repository::get_repository_by_id($repoid, $context);
+                    if (!empty($options)) {
+                        $repo->options = $options;
+                    }
+                    $file_record['repositoryid'] = $repoid;
+                    // This hook gives the repo a place to do some house cleaning, and update the $reference before it's saved
+                    // to the file store. E.g. transfer ownership of the file to a system account etc.
+                    $reference = $repo->reference_file_selected($file->get_reference(), $context, $component, $filearea, $itemid);
+
+                    $file_record['reference'] = $reference;
+                }
+            }
+
+            // Changes for PDFAnnotator.
+            $newentry = $fs->create_file_from_storedfile($file_record, $file);
+            // Checks if the file size exceeds the max size of files in the PDFAnnotator setting.
+            $maxfilesize = get_config('mod_pdfannotator', 'maxbytes');
+            if ($maxfilesize != 0 && $maxfilesize < $newentry->get_filesize()) {
+                $params = new stdClass();
+                $params->filename = $newentry->get_filename();
+                $params->filesize = $newentry->get_filesize();
+                $params->maxfilesize = $maxfilesize;
+                throw new  Error(get_string('error:maxsizeoffiles', 'mod_pdfannotator', $params));
+            }
+            // Changes for PDFAnnotator.
+            $embeddedfile_pdfannotator = new stdClass();
+            $embeddedfile_pdfannotator->fileid = $newentry->get_id();
+            $embeddedfile_pdfannotator->commentid = $itemid;
+            $embeddedfile_pdfannotator->id = $DB->insert_record('pdfannotator_embeddedfiles', $embeddedfile_pdfannotator);
+            // Set the sortorder for the mapping with pdfannotator_embeddedfiles table.
+            $newentry->set_sortorder($embeddedfile_pdfannotator->id);
+        }
+    }
+
+    // note: do not purge the draft area - we clean up areas later in cron,
+    //       the reason is that user might press submit twice and they would loose the files,
+    //       also sometimes we might want to use hacks that save files into two different areas
+
+    if (is_null($text)) {
+        return null;
+    } else {
+        return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
+    }
+}
+
 function pdfannotator_get_instance_name($id) {
 
     global $DB;
diff --git a/shared/index.js b/shared/index.js
index 9be76a86b217128c409366d33c7e0a613bbb9ec5..014762ddb1562950cdce5404060c0f4d5a0e4299 100644
--- a/shared/index.js
+++ b/shared/index.js
@@ -6024,11 +6024,13 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
             var rectObj;
             var _svg=void 0;
             var rect=void 0;
+
             /**
             * Get the current window selection as rects
             *
             * @return {Array} An Array of rects
-            */function getSelectionRects(){
+            */
+            function getSelectionRects(){
                 try{
                     var selection=window.getSelection();
                     try{
@@ -6048,11 +6050,13 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
 
                 }
                 return null;
-            }/**
+            }
+        /**
         * Handle document.mousedown event
         *
         * @param {Event} e The DOM event to handle
-        */function handleDocumentMousedown(e){
+        */
+        function handleDocumentMousedown(e){
             if(!(_svg=(0,_utils.findSVGAtPoint)(e.clientX,e.clientY))|| _type!=='area'){
                 return;
             }
@@ -6105,12 +6109,12 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
             (0,_utils.disableUserSelect)();
         }
 
-
         /**
         * Handle document.mousemove event
         *
         * @param {Event} e The DOM event to handle
-        */function handleDocumentMousemove(e){
+        */
+        function handleDocumentMousemove(e){
             if(originX+(e.clientX-originX)<rect.right){
                 overlay.style.width=e.clientX-originX+'px';
             }
@@ -6143,7 +6147,8 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
         * Handle document.mouseup event
         * concerns area,highlight and strikeout
         * @param {Event} e The DOM event to handle
-        */function handleDocumentMouseup(e){
+        */
+        function handleDocumentMouseup(e){
             //if the cursor is clicked nothing should happen!
             if((typeof e.target.getAttribute('className')!='string') &&  e.target.className.indexOf('cursor') === -1){
                 document.removeEventListener('mousemove',handleDocumentMousemove);
@@ -6280,7 +6285,8 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
         * Handle document.keyup event
         *
         * @param {Event} e The DOM event to handle
-        */function handleDocumentKeyup(e){// Cancel rect if Esc is pressed
+        */
+        function handleDocumentKeyup(e){// Cancel rect if Esc is pressed
             if(e.keyCode===27){var selection=window.getSelection();selection.removeAllRanges();if(overlay&&overlay.parentNode){overlay.parentNode.removeChild(overlay);overlay=null;document.removeEventListener('mousemove',handleDocumentMousemove);}}
         }
         
@@ -6403,28 +6409,31 @@ function startIndex(Y,_cm,_documentObject,_contextId, _userid,_capabilities, _to
         }
         /**
         * Enable rect behavior
-        */function enableRect(type){
-           _type=type;
-           if(_enabled){return;}
-           
-           if(_type === 'area'){
-               document.getElementById('content-wrapper').classList.add('cursor-area');
-           }else if(_type === 'highlight'){
-               document.getElementById('content-wrapper').classList.add('cursor-highlight');
-           }else if(_type === 'strikeout'){
-               document.getElementById('content-wrapper').classList.add('cursor-strikeout');
-           }
-           
-           _enabled=true;
-           document.addEventListener('mouseup',handleDocumentMouseup);
-           document.addEventListener('mousedown',handleDocumentMousedown);
-           document.addEventListener('keyup',handleDocumentKeyup);
-
-           document.addEventListener('touchstart', handleDocumentTouchstart);
-           document.addEventListener('touchend', handleDocumentTouchend);
-       }/**
+        */
+        function enableRect(type){
+            _type=type;
+            if(_enabled){return;}
+            
+            if(_type === 'area'){
+                document.getElementById('content-wrapper').classList.add('cursor-area');
+            }else if(_type === 'highlight'){
+                document.getElementById('content-wrapper').classList.add('cursor-highlight');
+            }else if(_type === 'strikeout'){
+                document.getElementById('content-wrapper').classList.add('cursor-strikeout');
+            }
+            
+            _enabled=true;
+            document.addEventListener('mouseup',handleDocumentMouseup);
+            document.addEventListener('mousedown',handleDocumentMousedown);
+            document.addEventListener('keyup',handleDocumentKeyup);
+
+            document.addEventListener('touchstart', handleDocumentTouchstart);
+            document.addEventListener('touchend', handleDocumentTouchend);
+        }
+        /**
         * Disable rect behavior
-        */function disableRect(){
+        */
+        function disableRect(){
             if(!_enabled){return;}
             _enabled=false;
             if(_type === 'area'){
diff --git a/version.php b/version.php
index 981da977622d52f7a22be2622839554f2398f859..1350aa61f7610854ec3aaf5a4fa92bb36b87c8ed 100644
--- a/version.php
+++ b/version.php
@@ -25,7 +25,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'mod_pdfannotator';
-$plugin->version   = 2022102602;
+$plugin->version   = 2022110200;
 $plugin->release  = 'PDF Annotator v1.4 release 11';
 $plugin->requires  = 2021051700;
 $plugin->maturity  = MATURITY_STABLE;