PDFtron: change name of element - pdftron

I'm using PDFTron's Java SDK, and I want to change the name of an element, then write the modified PDF to a new file, but I get the following output:
PDFNet is running in demo mode.
Permission: read
Exception:
Message: SetName() can't be invoked on Obj of this type.
How can I change an object's name? My code (in Scala) is as follows:
def main(args: Array[String]): Unit = {
PDFNet.initialize()
var doc = new PDFDoc("example.pdf")
var fdf = doc.fdfExtract
var iter = fdf.getFieldIterator
while (iter.hasNext) {
var field = iter.next
var obj = field.findAttribute("T")
if (obj != null && field.getName.startsWith("MyPrefix")) {
obj.setName("NewPrefix") // `field.setName` produces the same error
}
}
}

The API Field.GetName() is technically an amalgamation of this leaf Field and any parent ones, delimited by a ..
So while Field.getName() might return name.first the Field's T value might just be first. This is why there is Field.getPartialName() exists.
So the better/safer code to change the T value is.
var obj = field.findAttribute("T")
if (obj != null && obj.isString() && obj.getAsPDFText().startsWith("MyPrefix")) {
obj.setString("NewPrefix")
}

Related

Firestore doesn't support JavaScript objects with custom prototypes?

I'm using the node Bigquery Package, to run a simple job. Looking at the results (say data) of the job the effective_date attribute look like this:
effective_date: BigQueryDate { value: '2015-10-02' }
which is obviously an object within the returned data object.
Importing the returned json into Firestore gives the following error:
UnhandledPromiseRejectionWarning: Error: Argument "data" is not a
valid Document. Couldn't serialize object of type "BigQueryDate".
Firestore doesn't support JavaScript objects with custom prototypes
(i.e. objects that were created via the 'new' operator).
Is there an elegant way to handle this? Does one need to iterate through the results and convert / remove all Objects?
The firestore Node.js client do not support serialization of custom classes.
You will find more explanation in this issue:
https://github.com/googleapis/nodejs-firestore/issues/143
"We explicitly decided to not support serialization of custom classes for the Web and Node.JS client"
A solution is to convert the nested object to a plain object. For example by using lodash or JSON.stringify.
firestore.collection('collectionName')
.doc('id')
.set(JSON.parse(JSON.stringify(myCustomObject)));
Here is a related post:
Firestore: Add Custom Object to db
Another way is less resource consuming:
firestore
.collection('collectionName')
.doc('id')
.set(Object.assign({}, myCustomObject));
Note: it works only for objects without nested objects.
Also you may use class-transformer and it's classToPlain() along with exposeUnsetFields option to omit undefined values.
npm install class-transformer
or
yarn add class-transformer
import {classToPlain} from 'class-transformer';
firestore
.collection('collectionName')
.doc('id')
.set(instanceToPlain(myCustomObject, {exposeUnsetFields: false}));
If you have a FirebaseFirestore.Timestamp object then don't use JSON.parse(JSON.stringify(obj)) or classToPlain(obj) as those will corrupt it while storing to Firestore.
It's better to use {...obj} method.
firestore
.collection('collectionName')
.doc('id')
.set({...obj});
Note: do not use new operator for any nested objects inside document class, it'll not work. Instead, create an interface or type for nested object properties like this:
interface Profile {
firstName: string;
lastName: string;
}
class User {
id = "";
isPaid = false;
profile: Profile = {
firstName: "",
lastName: "",
};
}
const user = new User();
user.profile.firstName = "gorv";
await firestore.collection("users").add({...user});
And if you really wanna store class object consists of deeply nested more class objects then use this function to first convert it to plain object while preserving FirebaseFirestore.Timestamp methods.
const toPlainFirestoreObject = (o: any): any => {
if (o && typeof o === "object" && !Array.isArray(o) && !isFirestoreTimestamp(o)) {
return {
...Object.keys(o).reduce(
(a: any, c: any) => ((a[c] = toPlainFirestoreObject(o[c])), a),
{}
),
};
}
return o;
};
function isFirestoreTimestamp(o: any): boolean {
if (o &&
Object.getPrototypeOf(o).toMillis &&
Object.getPrototypeOf(o).constructor.name === "Timestamp"
) {
return true;
}
return false;
}
const user = new User();
user.profile = new Profile();
user.profile.address = new Address();
await firestore.collection("users").add(toPlainFirestoreObject(user));
Serializes a value to a valid Firestore Document data, including object and its childs and Array and its items
export function serializeFS(value) {
const isDate = (value) => {
if(value instanceof Date || value instanceof firestore.Timestamp){
return true;
}
try {
if(value.toDate() instanceof Date){
return true;
}
} catch (e){}
return false;
};
if(value == null){
return null;
}
if(
typeof value == "boolean" ||
typeof value == "bigint" ||
typeof value == "string" ||
typeof value == "symbol" ||
typeof value == "number" ||
isDate(value) ||
value instanceof firestore.FieldValue
) {
return value;
}
if(Array.isArray(value)){
return (value as Array<any>).map((v) => serializeFS(v));
}
const res = {};
for(const key of Object.keys(value)){
res[key] = serializeFS(value[key]);
}
return res;
}
Usage:
await db().collection('products').doc()
.set(serializeFS(
new ProductEntity('something', 123, FieldValue.serverTimestamp()
)));

Deeply nested data objects in multidimensional object

I have a multidimensional object and using Vue, I am trying to make the inner object reactive.
My object looks like this:
data() {
return {
myObject: {}
}
}
And the filled data looks like this:
myObject: {
1: { // (client)
0: "X", // (index) : (value)
1: "Y"
},
2: {
0: "A",
2: "B"
}
}
If I try using:
let value = "X";
let client = 1;
let index = 1;
let obj = {};
obj[client][index] = value;
this.myObject = Object.assign({}, this.myObject, obj);
It throws an error:
TypeError: Cannot set property '0' of undefined
And if I try below, it overwrites the initial values as it is initially setting the object to {}
let obj = {};
obj[index] = value;
let parentObj = {};
parentObj[client] = obj;
this.myObject = Object.assign({}, this.myObject, parentObj);
What is the proper way of adding the values to the multidimensional object?
In javascript, dim2Thing[1][1] = ... expressions require dim2Thing[1] to exist. This is why you get the error you mentioned. So you can do two expressions, which should work fine:
dim2Thing[1] = dim2Thing[1] || {}
dim2Thing[1][1] = otherThing
For the last block, you mention that it "overwrites the initial values"
I think what's actually happening here is just that Object.assign is not recursive. It only merges top-level keys. So if parentObj has a key that over-laps with this.myObj, then sub-keys will be lost.
Object.assign({ a: { b: 2} }, { a: { c: 3 } }) // returns { a: { c: 3 } }
This is what I interpret your code as trying to do - though I am unfamiliar with vue.js at this time, so I cannot assure it will have the desired result to your webpage:
let value = "X";
let client = 1;
let index = 1;
const newObj = Object.assign({}, this.myObject);
// if you have lodash _.set is handy
newObj[client] = newObj[client] || {}; // whatever was there, or a new object
newObj[client][index] = value
this.myObject = newObj
Just use an array, thats reactive by design.
If you need to get elements from the array in your template or anywhere just add a find method
// temp
late
<div v-for="(value, idx) in myArray">{{find(obj => obj.id === idx)}}</div>
methods: {
find (searchFunction) {
return this.myArray.find(searchFunction)
}
}

GenericUDF of hive execute twice on Spark

Hello i facing some problem with creating genericUDF of hive and register as temporary function but when i call it its call twice see code given below
i create a genericUDF with following code
class GenUDF extends GenericUDF{
var queryOI: StringObjectInspector = null
var argumentsOI: Array[ObjectInspector] = null
override def initialize (arguments: Array[ObjectInspector]):ObjectInspector = {
/*if (arguments.length == 0) {
throw new UDFArgumentLengthException("At least one argument must be specified")
}
if (!(arguments(0).isInstanceOf[StringObjectInspector])) {
throw new UDFArgumentException("First argument must be a string")
}
queryOI = arguments(0).asInstanceOf[StringObjectInspector]
argumentsOI = arguments*/
println("inside initializeweeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
return PrimitiveObjectInspectorFactory.javaStringObjectInspector
}
override def evaluate (arguments: Array[GenericUDF.DeferredObject]):Object = {
println("inside generic UDF::::::::::::::::::::::((((((((((((((((((((((((FDDDDDDDDDDDDD:")
4.toString
}
def getDisplayString(children: Array[String]): String = {
println("inside displayssssssssssssssssssssssssssssssss")
return "udft"
}
}
And when i register it with following statement
hiveContext.sql("CREATE TEMPORARY FUNCTION udft AS 'functions.GenUDF'")
and when i call this function with following command
select udft()
it will execute the print statement in evaluate body twice.

Orchard CMS- Get the current Data Migration Record version number

Given the name of a Migrations class as a string, how can I get the current version number as stored in Orchard_Framework_DataMigrationRecord?
I can see Version in IExtensionManager, but that appears to just be the module version as defined in module.txt.
OK, so I've solved this myself-
I knew that Orchard must already be executing similar code to what I require when it fires off migration methods, so I created a new migrations file, and put a breakpoint on the Create() method. When the breakpoint hit, I looked up through the call stack to find DataMigrationManager in Orchard.Data.Migration. Everything I needed was in there, and if anyone else has similar requirements, I suggest they have a look at that class as a starting point.
This is pretty much lifted straight out of that class:
string moduleName="Your.Module.Name";
var migrations = GetDataMigrations(moduleName);
// apply update methods to each migration class for the module
var current = 0;
foreach (var migration in migrations)
{
// copy the objet for the Linq query
var tempMigration = migration;
// get current version for this migration
var dataMigrationRecord = GetDataMigrationRecord(tempMigration);
if (dataMigrationRecord != null)
{
current = dataMigrationRecord.Version.Value;
}
// do we need to call Create() ?
if (current == 0)
{
// try to resolve a Create method
var createMethod = GetCreateMethod(migration);
if (createMethod != null)
{
//create method has been written, but not executed!
current = (int)createMethod.Invoke(migration, new object[0]);
}
}
}
Context.Output.WriteLine("Version: {0}", current);
A couple of methods you may need:
private DataMigrationRecord GetDataMigrationRecord(IDataMigration tempMigration)
{
return _dataMigrationRepository.Table
.Where(dm => dm.DataMigrationClass == tempMigration.GetType().FullName)
.FirstOrDefault();
}
private static MethodInfo GetCreateMethod(IDataMigration dataMigration)
{
var methodInfo = dataMigration.GetType().GetMethod("Create", BindingFlags.Public | BindingFlags.Instance);
if (methodInfo != null && methodInfo.ReturnType == typeof(int))
{
return methodInfo;
}
return null;
}
Don't forget to inject any dependencies that you may need.

Mapping umbraco node to strongtyped object

I am working with Umbraco 4.7.1 and I am trying to map the content-nodes to some autogenerated strong typed objects. I have tried using both valueinjecter and automapper, but OOTB neither of them map my properties. I guess it is because all properties on an Umbraco node (the cms document) are retrieved like this:
node.GetProperty("propertyName").Value;
And my strongly typed objects are in the format of MyObject.PropertyName. So how do I map the property on the node which is retrieved using a method and a string beginning with a lowercase character into a property on MyObject where the property begins with an uppercase character ?
UPDATE
I managed to create the following code which maps the umbraco node as intended, by digging around in the Umbraco sourcecode for some inspiration on how to cast string-properties to strongly typed properties:
public class UmbracoInjection : SmartConventionInjection
{
protected override bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override void Inject(object source, object target)
{
if (source != null && target != null)
{
Node node = source as Node;
var props = target.GetProps();
var properties = node.Properties;
for (int i = 0; i < props.Count; i++)
{
var targetProperty = props[i];
var sourceProperty = properties[targetProperty.Name];
if (sourceProperty != null && !string.IsNullOrWhiteSpace(sourceProperty.Value))
{
var value = sourceProperty.Value;
var type = targetProperty.PropertyType;
if (targetProperty.PropertyType.IsValueType && targetProperty.PropertyType.GetGenericArguments().Length > 0 && typeof(Nullable<>).IsAssignableFrom(targetProperty.PropertyType.GetGenericTypeDefinition()))
{
type = type.GetGenericArguments()[0];
}
targetProperty.SetValue(target, Convert.ChangeType(value, type));
}
}
}
}
}
As you can see I use the SmartConventionInjection to speed things up.
It still takes approximately 20 seconds to map something like 16000 objects. Can this be done even faster ?
thanks
Thomas
with ValueInjecter you would do something like this:
public class Um : ValueInjection
{
protected override void Inject(object source, object target)
{
var node = target as Node;
var props = source.GetProps();
for (int i = 0; i < props.Count; i++)
{
var prop = props[i];
target.GetProperty(prop.Name).Value;
}
}
}

Resources