Convert Images to PDF file in Lightning Web Component | LWC Stack | Salesforce ☁️⚡️



In this blog you will learn how you can convert image files like JPG & PNG to PDF file in Lightning web component. 
I will create a custom lightning web component to convert the files on file upload.

To import the library you can follow steps 1-3 from below link
https://www.salesforcebolt.com/2021/09/create-custom-PDF-in-Lightning-Web-Component.html

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
  <lightning-spinner
    if:true={isLoading}
    alternative-text="Loading"
    size="small"
  ></lightning-spinner>
  <div class="slds-m-around_small">
    <lightning-input
      label=""
      name="file uploader"
      onchange={handleFilesChange}
      type="file"
      multiple
    ></lightning-input>
  </div>
  <div class="slds-m-around_small">
    <template if:true={fileName}>
      {fileName} &nbsp; &nbsp; {fileSize}
    </template>
  </div>
</template>


JS
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { LightningElement, api } from "lwc";
import saveTheChunkFile from "@salesforce/apex/FileUploadService.saveTheChunkFile";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import PDF_LIB from "@salesforce/resourceUrl/pdflib";
import { loadScript } from "lightning/platformResourceLoader";
const MAX_FILE_SIZE = 4500000;
const CHUNK_SIZE = 750000;
export default class ConvertImagetoPDF extends LightningElement {
  @api recordId;

  fileName = "";
  filesUploaded = [];
  isLoading = false;
  fileSize;
  filetype;
  renderedCallback() {
    loadScript(this, PDF_LIB).then(() => {});
  }

  handleFilesChange(event) {
    if (event.target.files != null) {
      this.processFilesToConvert(event.target.files);
    }
  }
  async processFilesToConvert(files) {
    if (files.length > 0) {
      for (let i = 0; i < files.length; i++) {
        await this.createFileContent(files[i]);
      }
    }
  }
  async createFileContent(file) {
    this.showSpinner = true;
    this.fileName = file.name;
    this.filetype = file.type;
    var reader = new FileReader();
    var self = this;
    reader.onload = function () {
      var fileContents = reader.result;
      var base64Mark = "base64,";
      var dataStart = fileContents.indexOf(base64Mark) + base64Mark.length;
      fileContents = fileContents.substring(dataStart);
      if (self.filetype != "application/pdf")
        self.embedImageFile(fileContents, self.filetype);
      else self.prepareFileToUpload(fileContents);
    };
    reader.readAsDataURL(file);

    await new Promise((resolve, reject) => setTimeout(resolve, 3000));
  }
  async embedImageFile(file, filetype) {
    const pdfDoc = await PDFLib.PDFDocument.create();
    let imageFile = "";
    if (filetype == "image/png") imageFile = await pdfDoc.embedPng(file);
    else if (filetype == "image/jpeg") imageFile = await pdfDoc.embedJpg(file);

    let imageDims = imageFile;

    const page = pdfDoc.addPage();
    if (imageFile.width > 595) {
      const scaleValue = parseFloat(595 / imageFile.width);
      imageDims = imageFile.scale(scaleValue);
    }
    page.drawImage(imageFile, {
      x: 0,
      y: page.getHeight() - imageDims.height,
      width: imageDims.width,
      height: imageDims.height
    });
    const pdfBytes = await pdfDoc.save();
    this.prepareFileToUpload(pdfBytes);
  }
  prepareFileToUpload(pdfBytes) {
    var blob = new Blob([pdfBytes], { type: "application/pdf" });
    this.fileSize = this.formatBytes(blob.size, 2);
    if (blob.size > MAX_FILE_SIZE) {
      let message =
        "File size cannot exceed " +
        MAX_FILE_SIZE +
        " bytes.\n" +
        "Selected file size: " +
        blob.size;
      this.dispatchEvent(
        new ShowToastEvent({
          title: "Error",
          message: message,
          variant: "error"
        })
      );
      return;
    }
    var reader = new FileReader();
    var self = this;
    reader.onload = function () {
      var fileContents = reader.result;
      var base64Mark = "base64,";
      var dataStart = fileContents.indexOf(base64Mark) + base64Mark.length;
      fileContents = fileContents.substring(dataStart);
      if (self.filetype != "application/pdf") self.upload(blob, fileContents);
      else self.upload(blob, pdfBytes);
    };
    reader.readAsDataURL(blob);
  }
  upload(file, fileContents) {
    var fromPos = 0;
    var toPos = Math.min(fileContents.length, fromPos + CHUNK_SIZE);

    this.uploadChunk(file, fileContents, fromPos, toPos, "");
  }

  uploadChunk(file, fileContents, fromPos, toPos, attachId) {
    this.isLoading = true;
    var chunk = fileContents.substring(fromPos, toPos);

    saveTheChunkFile({
      parentId: this.recordId,
      fileName: file.name,
      base64Data: encodeURIComponent(chunk),
      contentType: file.type,
      fileId: attachId
    })
      .then((result) => {
        attachId = result;
        fromPos = toPos;
        toPos = Math.min(fileContents.length, fromPos + CHUNK_SIZE);
        if (fromPos < toPos) {
          this.uploadChunk(file, fileContents, fromPos, toPos, attachId);
        } else {
          this.dispatchEvent(
            new ShowToastEvent({
              title: "Success!",
              message: "File Upload Success",
              variant: "success"
            })
          );
          this.isLoading = false;
        }
      })
      .catch((error) => {
        console.error("Error: ", error);
      })
      .finally(() => {});
  }

  formatBytes(bytes, decimals) {
    if (bytes == 0) return "0 Bytes";
    var k = 1024,
      dm = decimals || 2,
      sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
      i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
  }
}


APEX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public with sharing class FileUploadService {
    
    @AuraEnabled                            
    public static Id saveTheChunkFile(Id parentId, String fileName, String base64Data, String contentType, String fileId){
        
        base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
        if ( String.isBlank(fileId) ) {
            fileId = saveFiles(parentId, fileName, base64Data );
        } else {
            appendToFile(fileId, base64Data);
        }

        return Id.valueOf(fileId);
    }

    @AuraEnabled
    public static Id saveFiles(Id recordId, String fileName, String base64Data )  { 
        
        ContentVersion contentToInsert =new ContentVersion(); 
        contentToInsert.Title =fileName; 
        contentToInsert.VersionData=EncodingUtil.base64Decode(base64Data);
        contentToInsert.PathOnClient='CustomPDF.pdf' ;
        contentToInsert.IsMajorVersion = false;
		contentToInsert.Title = 'ConvertedtoPDF';
	    insert contentToInsert; 
        
        contentToInsert = [select id, ContentDocumentId from ContentVersion WHERE Id =: contentToInsert.Id];
        ContentDocumentLink cl = new ContentDocumentLink();
        cl.ContentDocumentId = contentToInsert.ContentDocumentId;
        cl.LinkedEntityId = recordId; 
        cl.ShareType = 'V';
        cl.Visibility = 'AllUsers';
        insert cl;
        
        return contentToInsert.id;

    }


    @AuraEnabled
    public static void appendToFile(Id contentDocumentId, String base64Data) {
        //base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
        ContentVersion versionData = [
            SELECT Id, VersionData,ContentDocumentId
            FROM ContentVersion
            WHERE Id = :contentDocumentId
        ];

        String existingBody     = EncodingUtil.base64Encode(versionData.VersionData);
        versionData.VersionData = EncodingUtil.base64Decode(existingBody + base64Data); 

        update versionData;
    }

    @AuraEnabled
    public static List<FileDescriptor> getEntityRecordFiles(List<String> listIds) {
		List<FileDescriptor> files = new List<FileDescriptor>();
		Set<Id> recordIds = new Set<Id>();
		for(String i:listIds){
			recordIds.add(i);
		}
		if (recordIds.size() == 0) {
			return files;
		}
		Map<Id,ContentVersion> versionsMap = new Map<Id, ContentVersion>([
		SELECT Id,VersionData, Title, ContentDocumentId, FileExtension
		FROM ContentVersion
		WHERE Id = :recordIds AND IsLatest = TRUE
		WITH SECURITY_ENFORCED
		]);
		for(String versionId : listIds){
		ContentVersion cv =	versionsMap.get(versionId);
			FileDescriptor data = new FileDescriptor();
			data.Content = EncodingUtil.base64Encode(cv.VersionData);
			data.ContentType = ContentType(cv.FileExtension);
			data.ContentDocumentId = cv.ContentDocumentId;
			files.add(data);
		}
		
		return files;
	}
    public static string ContentType(string fileType) {
		switch on fileType.toLowerCase() {
			when 'docx' {
				return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
			}
			when 'csv' {
				return 'application/vnd.ms-excel';
			}
			when 'wav' {
				return 'audio/wav';
			}
			when 'wmv' {
				return 'video/x-ms-wmv';
			}
			when 'mp3' {
				return 'audio/mpeg';
			}
			when 'mp4' {
				return 'video/mp4';
			}
			when 'png' {
				return 'image/png';
			}
			when 'pdf' {
				return 'application/pdf';
			}
			when else {
				return 'image/jpeg';
			}
		}
	}
}


OUTPUT


Checkout complete video tutorial below

 If you have any question please leave a comment below.

If you would like to add something to this post please leave a comment below.
Share this blog with your friends if you find it helpful somehow !

Thanks
Happy Coding :)



Post a Comment

4 Comments

  1. Hi. seems like apex class attribute FileDescriptor can't found is salesforce "public static List is not , do you mind sharing

    ReplyDelete
    Replies
    1. Here you go
      public with sharing class FileDescriptor {
      @AuraEnabled
      public string content { get; set; }
      @AuraEnabled
      public string contentType { get; set; }
      @AuraEnabled
      public string contentDocumentId { get; set; }
      public FileDescriptor() {
      }
      }

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Bro how to add this component in screen flow and placed that quick actions button in work order object ?

      Delete