For Zoho Services only:

I'm actually part of something bigger at Ascent Business Solutions recognized as the top Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions offer support for smaller technical fixes and projects for larger developments, such as migrating to a ZohoCRM.  A team rather than a one-man-band is always available to ensure seamless progress and address any concerns. You'll find our competitive support rates with flexible, no-expiration bundles at  For larger projects, check our bespoke pricing structure and receive dedicated support from our hands-on project consultants and developers at

The team I manage specializes in coding API integrations between Zoho and third-party finance/commerce suites such as Xero, Shopify, WooCommerce, and eBay; to name but a few.  Our passion lies in creating innovative solutions where others have fallen short as well as working with new businesses, new sectors, and new ideas.  Our success is measured by the growth and ROI we deliver for clients, such as transforming a garden shed hobby into a 250k monthly turnover operation or generating a +60% return in just three days after launch through online payments and a streamlined e-commerce solution, replacing a paper-based system.

If you're looking for a partner who can help you drive growth and success, we'd love to work with you.  You can reach out to us on 0121 392 8140 (UK) or  You can also visit our website at

Zoho Creator: Get Images in Report to be Exported to PDF

This is an article to document how to produce a report which when exported to PDF will display images without a user wall.

Our client needs to send a report to suppliers that include photos of the faulty products. These display fine in a Zoho Creator report and even show up on the preview when exporting to PDF. But when you download the PDF and view this outside of the Zoho instances, the photos don't display.

This is because the photos were just links in the Zoho Creator image field, links to another Zoho app that is behind a user wall (ie. login and password as a Zoho User).

So I'll go over what happens in the real world process; then cover some technical information; and even include the function that makes it work:

Business Process
  1. Customer visits website to report a faulty product; using website form or chat.
  2. Customer can upload or send to agent to upload to a Zoho Desk support ticket as an attachment.
  3. The ticket will appear in a Quality Control report in Zoho Creator
  4. Staff can access the report via Zoho Creator and export it to PDF.
  5. They can then send the PDF to the supplier.

Technical Information
  • Automations in the background, on update of ticket or attachment webhook, will push some of the details of the ticket and all/any attachments to a Zoho Creator form.
  • The ticket form has the ticket reference, product information, factory/supplier, the fault/description, and a subform to a form called "Documents" linked by a bi-directional ticket lookup.
  • The document form is a subform derived from an existing form (called "Document"). Mainly, because I don't know how to refer to the public URL of a subform row other than making the subform link to an existing form and not a blank form.
  • The document form contains the fields: Ticket (lookup to Ticket form), Image, File, Local Image, Subform Row ID (prevents duplicates). Note: I create the form then add it as a subform on the parent form first, then add the lookup to the ticket field on the subform so that I get the popup to enable bi-directional rather than via the tickbox.
  • The below code is added to a trigger whenever the ticket form is submitted either on creation or modification.

The function to rule-them-all
This code was used for testing, then for correcting any historical records, and then when given just one ticket reference; will create or update the images associated in the subform:
void Standalone.fn_DownloadAndCacheAttachments(string p_TicketRef)
	v_TicketRef = ifnull(p_TicketRef,"");
	c_TicketRecords = QC_Report_Table[Ticket_Number == v_TicketRef];
	for each  c_TicketRecord in c_TicketRecords
		info "Ticket Ref: " + c_TicketRecord.Ticket_Number;
		v_CountImages = 0;
		v_CountNew = 0;
		for each  c_ImageRow in c_TicketRecord.Images
			v_ImageRowID = c_ImageRow.ID.toString();
			v_CountImages = v_CountImages + 1;
			v_ImageHtml = ifnull(c_ImageRow.Image,"");
				v_ImageSrc = v_ImageHtml.getSuffix("\"");
				v_ImageSrc = v_ImageSrc.getPrefix("\"");
					// use a try...catch as some file types may cause this to partially fail 
						v_CountNew = v_CountNew + 1;
						f_DownloadFile = invokeurl
							url :v_ImageSrc
							type :GET
						// add into subform (actual form "Document").  Uses row.ID as the identifier to prevent duplicates.
						c_CheckDoc = Document[Subform_Row_ID == v_ImageRowID];
						// if exists then update
						if(c_CheckDoc.count() > 0)
							v_UploadFileID = c_CheckDoc.ID;
							v_UploadFileID = insert into Document
						// store this after the record is created/updated (was getting error if included in record creation - if in same row).
						// c_ImageRow.File_field = f_DownloadFile;
						// let's get the local image cache reference so it is exportable to PDF
						c_Document = Document[ID == v_UploadFileID];
						// publish key of "Documents" report
						v_PublishKey = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
						v_DocFileName = ifnull(c_Document.File_field,"");
						// used for blocking certain file extensions (not used in this function)
						v_DocExtension = v_DocFileName.subString(v_DocFileName.lastIndexOf(".") + 1).toLowerCase();
						// All_Documents is the report, File_field is the field.
						v_ThisDocSrc = "" + zoho.appuri + "All_Documents/" + c_Document.ID + "/File_field/download/" + v_PublishKey + "?filepath=/" + v_DocFileName;
						// Store the link to the local file as the local image - this is where we could check on file extension and substitute with PDF, DOC, XLS extensions
						c_Document.Local_Image = "<img src=\"" + v_ThisDocSrc + "\" />";
						info "File: " + v_DocFileName;
					catch (e)
						info e.message;
		info "Created/Updated " + v_CountNew + " of " + v_CountImages;
		info "----------------";
  1.  void Standalone.fn_DownloadAndCacheAttachments(string p_TicketRef) 
  2.  { 
  3.      v_TicketRef = ifnull(p_TicketRef,"")
  4.      c_TicketRecords = QC_Report_Table[Ticket_Number == v_TicketRef]
  5.      for each  c_TicketRecord in c_TicketRecords 
  6.      { 
  7.          info "Ticket Ref: " + c_TicketRecord.Ticket_Number; 
  8.          v_CountImages = 0
  9.          v_CountNew = 0
  10.          for each  c_ImageRow in c_TicketRecord.Images 
  11.          { 
  12.              v_ImageRowID = c_ImageRow.ID.toString()
  13.              v_CountImages = v_CountImages + 1
  14.              v_ImageHtml = ifnull(c_ImageRow.Image,"")
  15.              if(v_ImageHtml.contains("\"")) 
  16.              { 
  17.                  v_ImageSrc = v_ImageHtml.getSuffix("\"")
  18.                  v_ImageSrc = v_ImageSrc.getPrefix("\"")
  19.                  if(!isBlank(v_ImageSrc)) 
  20.                  { 
  21.                      // use a try...catch as some file types may cause this to partially fail 
  22.                      try 
  23.                      { 
  24.                          v_CountNew = v_CountNew + 1
  25.                          f_DownloadFile = invokeUrl 
  26.                          [ 
  27.                              url :v_ImageSrc 
  28.                              type :GET 
  29.                              connection:"zdesk" 
  30.                          ]
  31.                          f_DownloadFile.setParamName("file")
  32.                          // 
  33.                          // add into subform (actual form "Document").  Uses row.ID as the identifier to prevent duplicates. 
  34.                          c_CheckDoc = Document[Subform_Row_ID == v_ImageRowID]
  35.                          // 
  36.                          // if exists then update 
  37.                          if(c_CheckDoc.count() > 0) 
  38.                          { 
  39.                              c_CheckDoc.Image=c_ImageRow.Image; 
  40.                              c_CheckDoc.File_field=f_DownloadFile; 
  41.                              v_UploadFileID = c_CheckDoc.ID; 
  42.                          } 
  43.                          else 
  44.                          { 
  45.                              v_UploadFileID = insert into Document 
  46.                              [ 
  47.                                  Added_User=zoho.loginuser 
  48.                                  Ticket_field=c_TicketRecord.ID 
  49.                                  Image=c_ImageRow.Image 
  50.                                  File_field=f_DownloadFile 
  51.                                  Subform_Row_ID=c_ImageRow.ID 
  52.                              ]
  53.                          } 
  54.                          // 
  55.                          // store this after the record is created/updated (was getting error if included in record creation - if in same row)
  56.                          // c_ImageRow.File_field = f_DownloadFile; 
  57.                          // 
  58.                          // let's get the local image cache reference so it is exportable to PDF 
  59.                          c_Document = Document[ID == v_UploadFileID]
  60.                          // 
  61.                          // publish key of "Documents" report 
  62.                          v_PublishKey = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  63.                          v_DocFileName = ifnull(c_Document.File_field,"")
  64.                          // 
  65.                          // used for blocking certain file extensions (not used in this function) 
  66.                          v_DocExtension = v_DocFileName.subString(v_DocFileName.lastIndexOf(".") + 1).toLowerCase()
  67.                          // 
  68.                          // All_Documents is the report, File_field is the field. 
  69.                          v_ThisDocSrc = "" + zoho.appuri + "All_Documents/" + c_Document.ID + "/File_field/download/" + v_PublishKey + "?filepath=/" + v_DocFileName; 
  70.                          // 
  71.                          // Store the link to the local file as the local image - this is where we could check on file extension and substitute with PDF, DOC, XLS extensions 
  72.                          c_Document.Local_Image = "<img src=\"" + v_ThisDocSrc + "\" />"
  73.                          info "file: " + v_DocFileName; 
  74.                      } 
  75.                      catch (e) 
  76.                      { 
  77.                          info e.message; 
  78.                      } 
  79.                  } 
  80.              } 
  81.          } 
  82.          info "Created/Updated " + v_CountNew + " of " + v_CountImages; 
  83.          info "----------------"; 
  84.      } 
  85.  } 
This function is added to the trigger of when a ticket form is submitted (created or edited).

Category: Zoho :: Article: 866

Credit where Credit is Due:

Feel free to copy, redistribute and share this information. All that we ask is that you attribute credit and possibly even a link back to this website as it really helps in our search engine rankings.

Disclaimer: Please note that the information provided on this website is intended for informational purposes only and does not represent a warranty. The opinions expressed are those of the author only. We recommend testing any solutions in a development environment before implementing them in production. The articles are based on our good faith efforts and were current at the time of writing, reflecting our practical experience in a commercial setting.

Thank you for visiting and, as always, we hope this website was of some use to you!

Kind Regards,

Joel Lipman

Related Articles

Joes Revolver Map


Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Donate to Joel Lipman via PayPal

Donate to Joel Lipman with Bitcoin bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

Donate to Joel Lipman with Ethereum 0xb038962F3809b425D661EF5D22294Cf45E02FebF
© 2024 Joel Lipman .com. All Rights Reserved.