pdf-lib: how to add custom font - node.js

My environment is node v12.16.1 with typescript added.
I am using pdf-lib v1.16.0 (https://www.npmjs.com/package/pdf-lib) in order to fill the form for given PDF file. Library source code can be found here https://github.com/Hopding/pdf-lib and more on docs here https://pdf-lib.js.org/.
Regarding fonts pdf-lib has a set of called StandardFonts which is provided inside the lib.
export enum StandardFonts {
  Courier = 'Courier',
  CourierBold = 'Courier-Bold',
  CourierOblique = 'Courier-Oblique',
  CourierBoldOblique = 'Courier-BoldOblique',
  Helvetica = 'Helvetica',
  HelveticaBold = 'Helvetica-Bold',
  HelveticaOblique = 'Helvetica-Oblique',
  HelveticaBoldOblique = 'Helvetica-BoldOblique',
  TimesRoman = 'Times-Roman',
  TimesRomanBold = 'Times-Bold',
  TimesRomanItalic = 'Times-Italic',
  TimesRomanBoldItalic = 'Times-BoldItalic',
  Symbol = 'Symbol',
ZapfDingbats = 'ZapfDingbats',
}
Very often you would have requirement to apply font which is not on the list. One example is when you have some user documents/forms to fill and then to apply electronical signature on it in order to approve them. Sometimes that electronic signature can be of a different font type, chosen by the user.
So, how we can add new font type of our choice?

From the specs https://www.npmjs.com/package/pdf-lib#embed-font-and-measure-text
pdf-lib relies on a sister module to support embedding custom fonts:
#pdf-lib/fontkit. You must add the #pdf-lib/fontkit module to your
project and register it using pdfDoc.registerFontkit(...) before
embedding custom fonts.
We have to npm i --save #pdf-lib/fontkit and we have to have source from where which we will read the font. In my case I have added .otf file in project and loaded font. Files are structured like on the image:
import path from 'path';
import fs from 'fs';
import {PDFDocument, PDFForm, StandardFonts, PDFFont} from 'pdf-lib';
import fontkit from '#pdf-lib/fontkit';
const pdfBytes = fs.readFileSync(path.join(__dirname, `/w_template/` + fileName + '.pdf'));
const pdfDoc = await PDFDocument.load(pdfBytes);
pdfDoc.registerFontkit(fontkit);
//load font and embed it to pdf document
const fontBytes = fs.readFileSync(path.join(__dirname, 'HouschkaHead-BoldItalic.otf'));
const customFont = await pdfDoc.embedFont(fontBytes);
const form = pdfDoc.getForm();
const textField = form.getTextField('signature');
textField.setFontSize(11);
textField.setText('stefan z');
textField.updateAppearances(customFont);
// form flatten is available from v1.16.0 and it makes form read-only (not editable)
form.flatten();
const modifiedPdf = await pdfDoc.save();
And this is the final result: check how the signature input form field is different from rest of input fields which are filled with default font
Bonus: if you want to play with color of the text of inputs in form, this is what I have found while digging more under the library source code (it might be not optimal, but it can give you starting point for more things):
import {setFillingRgbColor} from 'pdf-lib'
const textField = form.getTextField(fieldName);
const da = textField.acroField.getDefaultAppearance() ?? '';
const newDa = da + '\n' + setFillingRgbColor(1, 0, 0).toString();
textField.acroField.setDefaultAppearance(newDa);

If anyone is struggling to change the fontSize in filling text fields using the latest inbuilt function called setFontSize(). You can use this approach.
const { PDFDocument, setFontAndSize } = require('pdf-lib');
const pdfDoc = await PDFDocument.load(file);
const form = pdfDoc.getForm();
const textField = form.getTextField('106Mobile.1');
textField.setText('This works fine');
const da = textField.acroField.getDefaultAppearance() ?? '';
const newDa = da + '\n' + setFontAndSize('Courier', 8).toString(); //setFontAndSize() method came to resuce
textField.acroField.setDefaultAppearance(newDa);
I had to use this because I got an error saying
No /DA (default appearance) entry found for field: 106Title.1
when setting font sizes to some text fields (not all of it). But I degenerately had those text fields. Fixed it using the above workaround.

Related

Empty Adobe PDF fields stripped from document when returned from DocuSign

We use Adobe Acrobat to add fields to a PDF. We want to be able to access these fields and reference their exact location so we can stamp content in their location after getting the PDF back from DocuSign. We do this by manipulating the PDF's bytes using the C# ITextSharp Text PDF library. Unfortunately, opening the PDF in Adobe Acrobat once it's returned reveals that all fields are removed from our document.
Our C# populates most of these fields with text data from our database. 2 fields are left blank, because we want to use C# to superimpose a static image of someone's signature over their location after the document has been signed via DocuSign.
This is okay for what's populated before DocuSign gets our document, because removing the field doesn't visually omit data previously entered into these fields. This is NOT fine when it comes time for us to stamp our static signature image into the document. Our usage of ITextSharp relies on finding a field with a particular Adobe Acrobat ID, getting its location, and "stamping" a static image in that location.
Is there a way to tell DocuSign that we want to maintain all of our PDF fields, their locations, and their IDs?
public byte[] StampStaticSignature(byte[] documentBytes)
{
var signatureContainer = new SignatureContainer();
var signatureBytes = signatureContainer.GetSignatureBytes();
var reader = new PdfReader(documentBytes);
var updatedForm = new byte[] { };
using var stream = new MemoryStream();
var stamper = new PdfStamper(reader, stream);
var pdfFormFields = stamper.AcroFields;
var signatureImage = GetImageFromStream(signatureBytes);
//_adobeSignatureFields is a list of strings used to
//identify the signature fields by their ID set in Adobe Acrobat.
foreach (var signatureField in _adobeSignatureFields)
{
var sigPosition = pdfFormFields.GetFieldPositions(signatureField);
var page = sigPosition[0];
var x1 = sigPosition[1];
var y1 = sigPosition[2];
var x2 = sigPosition[3];
var y2 = sigPosition[4];
var contentBytes = stamper.GetOverContent((int)page);
var signatureFieldHeight = y2 - y1;
var signatureFieldWidth = x2 - x1;
signatureImage.ScaleToFit(signatureFieldWidth, signatureFieldHeight);
signatureImage.SetAbsolutePosition(x1, y1);
contentBytes.AddImage(signatureImage);
}
stamper.FormFlattening = false;
stamper.Close();
reader.Close();
updatedForm = stream.ToArray();
stream.Dispose();
return updatedForm;
}
Hank, as Inbar mentioned already we will need to examine your code and your API logs to see what could be going wrong. It is very hard to say what went wrong, especially when using 3rd party software like ITextSharp. I'd suggest opening a case with us by visiting our support center and adding a reference to case 09033616

Need to dispaly svg on an image in Xamarin

I have an url that gets team logos but it returns svg https://www.mlbstatic.com/team-logos/141.svg.
How can i display this in a Image for xamarin forms?
Searched and only found complex huge amounts of code.
looking for
Download image -- I have this but what do i need to save it in GetResponsestream preferrable i would like to stay in memory and not write to disk or file.
Attach it to an image to display.
Thanks.
Ok, thought i would post my solution here.
I used SkiSharp:
SkiaSharp.Extended.Svg.SKSvg svg = new SkiaSharp.Extended.Svg.SKSvg();
using (WebClient client = new WebClient())
{
// ie for theurl: https://www.mlbstatic.com/team-logos/141.svg
svg.Load(new MemoryStream(client.DownloadData(new Uri(theurl))));
var bitmap = new SKBitmap((int)svg.CanvasSize.Width, (int)svg.CanvasSize.Height);
var canvas = new SKCanvas(bitmap);
canvas.DrawPicture(svg.Picture);
canvas.Flush();
canvas.Save();
string filename = "";
using (var image = SKImage.FromBitmap(bitmap))
using (var data = image.Encode(SKEncodedImageFormat.Png, 80))
{
// save the data to a stream
filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "temp.png");
using (var stream = File.OpenWrite(filename ))
{
data.SaveTo(stream);
}
}
}
use FileName from above to assign source to Xamarin image.
this accomplished the task with the least amount of code lines i tried.

Adding section icons and changing background color in dynamics CRM

I need to give an icon to each section in my entities. For example, I need to give an icon to General information, another one for interactions section.
Is there an idea about how could I do that? and how can I make a background color for each section please?
Thanks in advance,
There is no way to assign icons to sections. The best you could do would be to add a web resource to each of your sections and have them link to an image, but it doesn't really sound like that's what you're going for.
There are no supported ways to modify the form background color. If you don't care about remaining supported, though, you can use jQuery to do it. Put this function into your form script:
function changeSectionBackgroundColor(sectionId, color) {
parent.$("table.ms-crm-FormSection[name='"+sectionId+"']").css({"background-color": color});
}
and use it like this:
changeSectionBackgroundColor("General_Section_2", "red");
changeSectionBackgroundColor("General_Section_2", "#ababab");
You could try to do something like this to insert the Section images:
var stackoverflow = (function (Xrm)
{
var sectionBarClassName = '.ms-crm-Form-SectionBar'; // name of the CSS class given to the Section's Label's <td> element
function GetSection(tabName, sectionName)
{
var parentTab = Xrm.Page.ui.tabs.getByName(tabName); // get the tab
var section = parentTab.sections.getByName(sectionName); // get the section
return section;
}
function AddSectionImage(tabName, sectionName, imageUrl)
{
var section = GetSection(tabName, sectionName); // retrieve section using Xrm
var elSection = document.querySelector('table[name=' + section.getKey() + ']');
var elSectionHeader = elSection.querySelector('tr:first-child');
var elTitles = elSection.querySelectorAll(sectionBarClassName);
if (elTitles.length === 1) // we can assume that this section has a title
{
var elImg = document.createElement('img');
elImg.src = imageUrl;
elTitles[0].insertBefore(elImg, elTitles[0].firstChild);
}
}
return {
AddSectionImage : AddSectionImage
};
})(Xrm);
You then call this code and pass in the Name (not label) of the tab and section as well as the relative URL of the image you want displayed. Like so:
stackoverflow.AddSectionImage('tab_5', 'tab_5_section_1', '/imgs/Cancel_16.png');
I've only tested this code in CRM 2016 (online). And the image is a bit rough. You'll need to take care of the styling (inline) and the size yourself.
This is of course, unsupported by Microsoft :)

How to use Maki svg icons with Snap.svg?

I'm experimenting with Snap in order to use svg and need to be able to use the Maki icons defined in https://github.com/mapbox/maki.
My plan is to load the svg's I need, and then instantiate them for particular icons on a piece of Snap paper. But in order for this to work, I need to place the icon at a particular place on the paper, but I can't get translation to work. Neither one of the translation techniques below works; the code works as is, but always places the icon at the top left.
What am I missing? There's not enough documentation on Snap, and I don't know if the problem is with the way the Maki icon svg is defined, or my use of Snap.
var icon = Snap.load("maki/bicycle-24.svg", function(f) {
var g = f.select("g").clone();
// var g = f.select("#layer1").clone(); // also works
// g.transform("t120,120");
// var t = new Snap.Matrix();
// t.translate(120,120);
// g.transform(t);
paper.append(g);
});
The cloning needs to happen after the append, as when loading an svg in Snap its just a fragment.
So you will need to do something like...
paper.append(f);
var element = paper.select('#someId').clone();
element.transform( myTransform );
Thank you! That did the trick! And since Snap is so poorly documented, I'm going to insert the code here that allows a general solution.
// Base set from which markers are constructed
var iconSet = paper.group();
iconSet.attr({ class: 'hide' });
// Instantiations of icons
var markers = paper.g();
// Now, create SVG shape
var icon = Snap.load("maki/bicycle-24.svg", function(icon) {
// Add it to the icon set
iconSet.append(icon);
// Instantiate it and remove from main view
var element = paper.select('#svg4460'); // Copies it!
// var element = paper.select('#base'); // Selects something but doesn't work
// var element = paper.select('#layer1'); // Selects something but doesn't work
// var element = paper.select('#bicycle-24'); // Silent fail
element = element.clone();
element.remove();
// Clone this icon and move it
var t = new Snap.Matrix();
t.translate(10,120);
element.transform(t);
// Insert into main document view (markers)
markers.add(element);
});

appcelerator titanium base64 encode blob objects

I am developing a mobile (iphone/android) application using appcelerator titanium (sdk 1.6.2).
At a certain point in the app the user choses an image that should be shown in an imageView, base64 encoded, then uploaded to my server.
The problem is the success event of the photo gallery returns the selected image as a blob object and the Titanium.Utils.base64encode method only accepts string values!
Is there any way to convert Titanium.Blob objects to strings?
Here is the code snippet:
var imageView = Titanium.UI.createImageView({
height:200,
width:200,
top:20,
left:10,
backgroundColor:'#999'
});
Titanium.Media.openPhotoGallery({
success:function(event)
{
var cropRect = event.cropRect;
var image = event.media;//blob object
// set image view
Ti.API.debug('Our type was: '+event.mediaType);
if(event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO)
{
imageView.image = image;// this works
var imgStr=Ti.Utils.base64encode(image);// doesn't work because 'image' has to be a string!, but how?
}
else
{
}
Titanium.API.info('PHOTO GALLERY SUCCESS cropRect.x ' + cropRect.x + ' cropRect.y ' + cropRect.y + ' cropRect.height ' + cropRect.height + ' cropRect.width ' + cropRect.width);
},
allowEditing:true,
popoverView:popoverView,
arrowDirection:arrowDirection,
mediaTypes:[Ti.Media.MEDIA_TYPE_VIDEO,Ti.Media.MEDIA_TYPE_PHOTO]
});
Thank you,
var imgStr=Ti.Utils.base64encode(image.toString());
.toString() converts something to a string representation
This worked for me.
var image = event.media;
var imgStr = Ti.Utils.base64encode(image).toString();
i just posted some code for a module to do this conversion, I know a patch is coming from appcelerator, but the module might be useful to you now.
Clearly Innovative Thoughts - Titanium Appcelerator Quickie: base64encode iOS Module

Resources