0

I am experiencing an issue when using Syncfusion PDF Viewer with PDF files after making edits using the Draw Ink tool or any other tools :

  1. I opened a PDF file using PDF Viewer.

  2. I added text using the Draw Ink tool.

  3. I then downloaded the modified file, and everything appeared correctly, including the text I added.

  4. However, when opening the file the next day using PDF Viewer, I noticed that the signature no longer appears in PDF Viewer, even though it exists in the file if opened with another PDF reader.

It seems there is an issue with how signatures or Draw Tool additions are rendered or saved after reopening the file in PDF Viewer.

Could you please advise if there is a solution for this issue or a way to ensure that signatures are permanently visible in PDF Viewer?



        [HttpPost("Load")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> Load()
        {
            Console.WriteLine("Load called");

            string body;
            using (var r = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false))
                body = await r.ReadToEndAsync();

            if (string.IsNullOrWhiteSpace(body))
                return BadRequest("Empty body");

            var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(body)
                       ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            if (!json.TryGetValue("document", out var doc) || string.IsNullOrWhiteSpace(doc))
                return BadRequest("Missing 'document'");

            // build data object from incoming json (preserve incoming hashId if present)
            var data = new Dictionary<string, string>(json, StringComparer.OrdinalIgnoreCase);

            // If client passed hashId and we have cached annotation data, attach it
            if (data.TryGetValue("hashId", out var incomingHash) && !string.IsNullOrWhiteSpace(incomingHash))
            {
                if (_cache.TryGetValue($"pdf:ann:{incomingHash}", out string annBase64) && !string.IsNullOrWhiteSpace(annBase64))
                {
                    data["importedData"] = annBase64;
                    data["annotationDataFormat"] = "Json";
                    Console.WriteLine($"Load: attached importedData for hashId={incomingHash} (len={annBase64.Length})");
                }
                else
                {
                    Console.WriteLine($"Load: no cached annotations for hashId={incomingHash}");
                }
            }

            byte[] bytes;
            if (IsHttpUrl(doc))
            {
                var bust = doc.Contains("?") ? "&" : "?";
                var noCacheUrl = doc + bust + "v=" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

                using var http = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All });
                http.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue
                {
                    NoCache = true,
                    NoStore = true,
                    MaxAge = TimeSpan.Zero
                };

                bytes = await http.GetByteArrayAsync(noCacheUrl);
                if (bytes == null || bytes.Length == 0)
                    return BadRequest("Remote document returned empty content.");
            }
            else if (IsDataUrl(doc))
            {
                var comma = doc.IndexOf(',');
                if (comma < 0) return BadRequest("Invalid data URL.");
                var b64 = doc[(comma + 1)..];
                bytes = Convert.FromBase64String((b64));
            }
            else if (LooksLikeBase64(doc))
            {
                bytes = Convert.FromBase64String((doc));
            }
            else
            {
                var localPath = Path.IsPathRooted(doc)
                              ? doc
                              : Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "wwwroot", "Data", doc));
                if (!System.IO.File.Exists(localPath))
                    return NotFound($"File not found: {localPath}");
                bytes = await System.IO.File.ReadAllBytesAsync(localPath);
                if (!data.ContainsKey("fileName"))
                    data["fileName"] = Path.GetFileName(localPath);
            }



            var pdfviewer = new PdfRenderer(_cache);
            object jsonResult;

            var stream = new MemoryStream(bytes);
            jsonResult = pdfviewer.Load(stream, data);

            Console.WriteLine("Load jsonResult: " + JsonConvert.SerializeObject(jsonResult));


            return Content(JsonConvert.SerializeObject(jsonResult), "application/json; charset=utf-8");
        }


let viewer = null;
let pdfUrlExample = '';

var currentPdfName = '';
var currentHashId = null;
var currentPdfLink = '';

function initializePdfViewer() {
    try {
        console.log('initializePdfViewer: start');

        if (typeof ej === 'undefined' || !ej.pdfviewer || !ej.base) {
            throw new Error('Syncfusion EJ2 scripts not loaded (ej is undefined). Ensure scripts are included before this file.');
        }
        if (typeof syncfusion_key === 'undefined') {
            console.warn('initializePdfViewer: syncfusion_key is undefined — registerLicense may fail');
        } else {
            try { ej.base.registerLicense(syncfusion_key); } catch (e) { console.warn('registerLicense failed', e); }
        }

        const el = document.querySelector('#pdfViewer');
        if (!el) {
            throw new Error('Missing DOM element with id="pdfViewer". Create <div id="pdfViewer"></div> in your page.');
        }

        ej.pdfviewer.PdfViewer.Inject(
            ej.pdfviewer.Toolbar,
            ej.pdfviewer.Magnification,
            ej.pdfviewer.BookmarkView,
            ej.pdfviewer.ThumbnailView,
            ej.pdfviewer.TextSelection,
            ej.pdfviewer.TextSearch,
            ej.pdfviewer.Print,
            ej.pdfviewer.Navigation,
            ej.pdfviewer.LinkAnnotation,
            ej.pdfviewer.Annotation,
            ej.pdfviewer.FormFields,
            ej.pdfviewer.FormDesigner,
            ej.pdfviewer.Download
        );

        const saveItem = {
            id: 'saveToServer',
            text: 'Save',
            tooltipText: 'Save to server',
            prefixIcon: 'e-icons e-save',
            overflow: 'Show'
        };

        viewer = new ej.pdfviewer.PdfViewer({
            serviceUrl: '/PdfViewer',
            resourceUrl: 'https://cdn.syncfusion.com/ej2/23.2.6/dist/ej2-pdfviewer-lib',
            documentPath: pdfUrlExample,
            ajaxRequestSuccess: function (args) {
                try {
                    if (args && args.action === 'Load') {
                        console.log('ajaxRequestSuccess.Load args.data=', args.data);
                        currentHashId = args.data?.hashId || args.data?.documentId || null;
                        if (args.data?.documentName) currentPdfName = args.data.documentName;
                    }
                } catch (err) {
                    console.error('ajaxRequestSuccess handler error', err);
                }
            },
            documentLoad: function (args) {
                if (args.documentName) currentPdfName = args.documentName;
            },
            serverActionSettings: {
                load: 'Load',
                renderPages: 'RenderPdfPages',
                renderTexts: 'RenderPdfTexts',
                renderThumbnail: 'RenderThumbnailImages',
                download: 'Download',
                print: 'PrintImages',
                exportAnnotations: 'ExportAnnotations',
                importAnnotations: 'ImportAnnotations',
                annotations: 'RenderAnnotationComments',
                unload: 'Unload'
            },
            enableToolbar: true,
            toolbarSettings: {
                showTooltip: true,
                toolbarItems: [
                    saveItem,
                    'OpenOption',
                    'PageNavigationTool',
                    'MagnificationTool',
                    'PanTool',
                    'SelectionTool',
                    'SearchOption',
                    'PrintOption',
                    'DownloadOption',
                    'UndoRedoTool',
                    'AnnotationEditTool',
                    'FormDesignerEditTool',
                    'CommentTool'
                ]
            }
        });

        viewer.toolbarClick = function (args) {
            if (args.item.id !== 'saveToServer') return;

            const recordId = Number($('#DocumentIdForPdfPreView').val()) || 0;
            const docType = $('#DocumentTypeForPdfPreView').val();
            const actionId = Number($('#DocumentUserActionIdForPdfPreView').val()) || 0;

            const fileName = (currentPdfName && currentPdfName !== 'document.pdf')
                ? currentPdfName
                : getFileNameFromUrl(viewer.documentPath);

            if (!currentHashId) {
                displayErrorAlert('لا يوجد hashId للملف الحالي. الرجاء فتح الملف من المصدر الذي يُعيد hashId.');
                return;
            }

            saveAnnotationsAndUpload({ recordId, docType, actionId, fileName, currentHashId })
                .then(res => {
                    if ((res?.data && typeof res.data === 'string' && res.data.includes('OK')) || res?.isSussccful === true) {
                        $('#PdfPreviewButtonModal').click();
                        displaySuccessfulAlert('...تم الحفظ');
                    } else {
                        displayErrorAlert(res?.message || 'حدث خطأ أثناء الحفظ');
                    }
                })
                .catch(err => {
                    console.error(err);
                    displayErrorAlert(err?.message || err);
                });
        };

        viewer.appendTo('#pdfViewer');

        $('div').filter(function () {
            return $(this).text().includes('This application was built using a trial version of Syncfusion');
        }).remove();

        console.log('initializePdfViewer: done, viewer=', viewer);
    } catch (err) {
        console.error('initializePdfViewer failed:', err);
        viewer = null;
        throw err;
    }
}

function ensureViewerInitialized(timeoutMs = 5000) {
    return new Promise((resolve, reject) => {
        if (viewer) return resolve(viewer);

        try {
            initializePdfViewer();
        } catch (initErr) {
            return reject(initErr);
        }

        const start = Date.now();
        const t = setInterval(() => {
            if (viewer) {
                clearInterval(t);
                return resolve(viewer);
            }
            if (Date.now() - start > timeoutMs) {
                clearInterval(t);
                return reject(new Error('Viewer init timed out'));
            }
        }, 50);
    });
}

async function setPdfToPdfViewer(url, dmsId, documentId, documentActionId, documentType, incomingHashId = window.currentHashId) {
    try {
        await ensureViewerInitialized();
    } catch (err) {
        console.error('setPdfToPdfViewer: ensureViewerInitialized failed', err);
        displayErrorAlert('فشل تهيئة عارض الـ PDF. تحقق من تحميل مكتبات Syncfusion ووجود العنصر #pdfViewer.');
        return;
    }

    try {
        if (!viewer) {
            console.error('setPdfToPdfViewer: viewer is still null after initialization attempt');
            return;
        }


        console.log('setPdfToPdfViewer incomingHashId=', incomingHashId, 'currentHashId=', window.currentHashId, 'url=', url);

        if (typeof incomingHashId !== 'undefined' && incomingHashId !== null && incomingHashId !== '') {
            window.currentHashId = incomingHashId;
        }

        if (viewer) {
            viewer.ajaxRequestInitiate = function (args) {
                if (args.action === 'Load') {
                    // دائماً اجعل Load جديد - لا ترسل hashId تلقائياً
                    const base = Object.assign({}, args.customData || {});
                    base.documentId = crypto.randomUUID();      // معرف تتبعي جديد
                    base.cacheBuster = Date.now().toString();   // يكسر الـ HTTP cache
                    base.fileName = currentPdfName || getFileNameFromUrl(url);
                    console.log('ajaxRequestInitiate (Load) customData=', base);
                    args.customData = base;
                }
            };
        } else {
            console.error('setPdfToPdfViewer: viewer unexpectedly null before ajaxRequestInitiate assignment');
        }


        try { viewer.unload(); } catch (e) { }
        const bustedUrl = url + (url.includes('?') ? '&' : '?') + 'v=' + Date.now();
        viewer.load(bustedUrl);

        currentPdfName = getFileNameFromUrl(url);

        $('#DocumentTypeForPdfPreView').val(documentType);
        $('#DocumentIdForPdfPreView').val(documentId);
        $('#DocumentUserActionIdForPdfPreView').val(documentActionId);
    } catch (err) {
        console.error('setPdfToPdfViewer failed', err);
    }
}

function getFileNameFromUrl(url, fallback = 'document.pdf') {
    if (!url) return fallback;
    currentPdfLink = url;
    const clean = url.split('#')[0].split('?')[0];
    let name = clean.split('/').pop();
    if (!name) return fallback;
    name = decodeURIComponent(name);
    return name || fallback;
}

function exportAnnotationsViaServer(hashId) {
    return fetch('/PdfViewer/ExportAnnotations', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ hashId, annotationDataFormat: 'Json' })
    }).then(async (resp) => {
        const txt = await resp.text();
        if (!resp.ok) throw new Error(txt || `${resp.status} ${resp.statusText}`);
        if (txt.startsWith('data:')) {
            const b64 = txt.split(',')[1] || '';
            return atob(b64);
        }
        return txt;
    });
}

function toBase64Utf8(str) {
    return btoa(unescape(encodeURIComponent(String(str ?? ''))));
}

function buildImportedDataBase64(annText) {
    const toBase64Utf8Local = (s) => btoa(unescape(encodeURIComponent(String(s ?? ''))));
    let text = String(annText || '');
    if (text.startsWith('data:')) {
        const comma = text.indexOf(',');
        const meta = text.substring(5, comma);
        const payload = text.substring(comma + 1);
        if (/;base64/i.test(meta)) return payload;
        return toBase64Utf8Local(decodeURIComponent(payload));
    }
    if (/^\s*[\{\[]/.test(text)) return toBase64Utf8Local(text);
    if (/^[A-Za-z0-9+/]+={0,2}$/.test(text.replace(/\s/g, ''))) return text.replace(/\s/g, '');
    return toBase64Utf8Local(text);
}

function normalizeBase64ForUpload(b64) {
    if (!b64) return '';
    b64 = String(b64).trim();
    const commaIdx = b64.indexOf(',');
    if (commaIdx >= 0) b64 = b64.substring(commaIdx + 1);
    b64 = b64.replace(/\s+/g, '');
    b64 = b64.replace(/-/g, '+').replace(/_/g, '/');
    const mod4 = b64.length % 4;
    if (mod4 === 1) {
        console.warn('normalizeBase64ForUpload: length % 4 == 1, trimming last char as fallback');
        b64 = b64.slice(0, b64.length - 1);
    } else if (mod4 > 0) {
        b64 += '='.repeat(4 - mod4);
    }
    return b64;
}

async function saveAnnotationsAndUpload({ recordId, docType, actionId, fileName, currentHashId }) {
    if (!currentHashId) throw new Error('missing hashId');

    const annText = await exportAnnotationsViaServer(currentHashId);
    const importedData = buildImportedDataBase64(annText);

    await fetch('/PdfViewer/ImportAnnotations', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            hashId: String(currentHashId),
            fileName: fileName,
            importedData: importedData,
            annotationDataFormat: 'Json',
            recordId: String(recordId || 0),
            documentsUserActionsId: String(actionId || 0),
            fileType: 'pdf',
            documentType: String(docType || '')
        })
    }).then(async resp => {
        if (!resp.ok) throw new Error(await resp.text());
    });

    const downloadResp = await fetch('/PdfViewer/Download', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ hashId: String(currentHashId), fileName: fileName, fileType: 'pdf' })
    });

    if (!downloadResp.ok) throw new Error(await downloadResp.text());
    const mergedBase64 = await downloadResp.text();

    const filesWithIds = [{
        RecordId: recordId,
        FileContent: normalizeBase64ForUpload(mergedBase64),
        FileName: fileName,
        FileType: 'pdf',
        DocumentType: docType,
        HeaderRecordId: 0,
        DocumentsUserActionsId: actionId
    }];

    const uploadRes = await runEndpointWithPostJson('/Master/UploadActionDmsList', filesWithIds);


    if ((uploadRes?.data && uploadRes.data.includes('OK')) || uploadRes?.isSussccful === true) {
        $('#PdfPreviewButtonModal').click();
        displaySuccessfulAlert('تم حفظ الـ PDF بكل التعديلات (يشمل الـ Ink).');
    } else {
        displayErrorAlert(uploadRes?.message || 'حدث خطأ أثناء الحفظ');
    }

    return uploadRes;
}

Thank you

0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.