Friday, September 16, 2011

Google+: Le mie Attività - Gadget per il tuo sito

Come promesso. Un po' di smanettamento e via con una mia prima applicazione di esempio delle API per Google+.
Una cosa interessante della chiamata per le attività di un utente è la possibilità di definire nell'URL la funzione javascript che deve essere chiamata al rientro della risposta JSON.
Il nostro json, infatti, non è così immediatamente utilizzabile (e di certo esistono molti più esperti di me, nel settore, ma io l'ho scoperto pian pianino) e ha bisogno di un po' di aiuto, sopratutto quando si tratta di chiamate cross-domain.
Bene. Jquery ci aiuta, sì e no: ho usato le librerie JSONP, che ho linkato direttamente da http://jquery-jsonp.googlecode.com/files/jquery.jsonp-2.1.4.min.js.

La chiamata, così, è composta in questo modo:

$(document).ready(
 function () {
   $.jsonp({
      url: "https://www.googleapis.com/plus/v1/people/100661704413873131754/activities/public?callback=readJSON&key=YOUR_APIKEY"
   });
});

Nell'URL notiamo il parametro "callback" che definisce quale funzione javascript chiamare al ritorno.

La funzione readJSON riceve un oggetto JSON che si può interpretare poi facilmente per scriverne il contenuto.

Il risultato è quello che vedete qui a fianco, nel gadget HTML con il mio codice.

Beh, un primo esempietto :-)
Qui sotto la funzione che ho utilizzato:

function readJSON(json) {
               // ciclo l'array
               var items = json.items;
               for (i = 0; i < items.length; i++) {
                   var item = items[i];
                   var attachments = item.object.attachments;
                   var plusoners = item.object.plusoners.totalItems;
                   var content = '';
                   var img = '';
                   var imgurl = '';
                   var title = '';
                   var text = '';
                   var exturl = '';
                   if (attachments) {
                       for (j = 0; j < attachments.length; j++) {
                           var objAttachment = attachments[j];
                           var objType = objAttachment.objectType;
                           if (objType == 'article') {
                               title = objAttachment.displayName;
                               text = objAttachment.content;
                               exturl = objAttachment.url;
                           }
                           if (objType == 'photo') {
                               imgurl = objAttachment.image.url;
                           }
                       }
                   }
                   else {
                       title = item.title;
                       exturl = item.url;
                   }
                   content = '<div id="' + i + '" class="post">';
                   content += '<h3><a href="' + item.url + '"  title="' + title + '" target="_blank">' + title + ' (' + plusoners + ')</a></h3>';
                   if (imgurl != '')
                       content += '<img src="' + imgurl + '" alt="' + title + '"/>';
                   if(text != '')
                       content +=  text;                  
                   content += '</div>';
                   $('#content').append(content);
               }
           }

Estrarre immagini da PDF o convertire pagine PDF in immagini in C#

E' stata dura. Molto dura. Una nuova sfida: Estrarre immagini da un PDF o convertire una pagina PDF in una immagine. Con C#.

Ho dovuto guardarmi parecchi forum e blog per arrivare ad una soluzione decente, free e con poco sforzo.

Partiamo dal primo caso: Estrazione di una immagine da una pagina di un documento PDF.
Ho utilizzato iTextSharp, che normalmente usiamo per generare PDF, ma che è stato utile per leggere il file e trovare in esso le immagini.

Il metodo qui sotto ritorna lo stream dell'immagine.. perché a me serviva così.
Vediamo il codice (dopo aver referenziato la DLL di iTextSharp):

using iTextSharp.text;
using iTextSharp.text.pdf;

static Stream ExtractImagesFromPDF(string sourcePdf)
{
  System.IO.MemoryStream streamImage = new MemoryStream();
  // NOTE: This will only get the first image it finds per page.
  PdfReader pdf = new PdfReader(sourcePdf);
  RandomAccessFileOrArray raf = new iTextSharp.text.pdf.RandomAccessFileOrArray(sourcePdf);
  int numbOfPages = 1; //or you can set = pdf.NumberOfPages and do it foreach page;
  try
  {
    PdfDictionary pg = pdf.GetPageN(numbOfPages);
    PdfDictionary res = (PdfDictionary)PdfReader.GetPdfObject(pg.Get(PdfName.RESOURCES));
    PdfDictionary xobj = (PdfDictionary)PdfReader.GetPdfObject(res.Get(PdfName.XOBJECT));

    if (xobj != null)
    {
      foreach (PdfName name in xobj.Keys)
      {
        PdfObject obj = xobj.Get(name);
        if (obj.IsIndirect())
        {
          PdfDictionary tg = (PdfDictionary)PdfReader.GetPdfObject(obj);
          PdfName type = (PdfName)PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE));
          if (PdfName.IMAGE.Equals(type))
          {
            int XrefIndex = Convert.ToInt32(((PRIndirectReference)obj).Number.ToString(System.Globalization.CultureInfo.InvariantCulture));
            PdfObject pdfObj = pdf.GetPdfObject(XrefIndex);
            PdfStream pdfStrem = (PdfStream)pdfObj;
            byte[] bytes = PdfReader.GetStreamBytesRaw((PRStream)pdfStrem);
            if ((bytes != null))
            {
              streamImage = new System.IO.MemoryStream(bytes);
              break;
            }
          }
       }
    }
   }
 }
 catch(Exception ex)
 { 
   Console.Write("Error extracting image from PDF : " + ex.Message);
 } 
 finally
 {
   pdf.Close();
 }
 return streamImage;
}

Quello che fa non è altro che leggere la prima pagina (ma è possibile ciclare e salvarsi un array di immagini) ed estrarre l'oggetto di tipo IMAGE del PDF.
Ma, ho incontrato un po' di difficoltà.
Innanzitutto, si presuppone che il PDF sia "ben fatto", quindi abbia effettivamente la struttura che leggiamo.
Questa struttura, ad es., viene correttamente generata dal "Save as PDF" di MS Office 2007/2010.
Purtroppo, però, alcuni PDF non sono così semplici da trattare.
Ad es., ho un PDF che ha una immagine grande: probabilmente generato da "Save as PDF" da Photoshop: non ha quindi la struttura letta comodamente con iTextSharp (ovvero è nullo l'XObject del PDF).

Allora, passiamo al secondo metodo: Convertire una pagina del PDF in una immagine.
Per questo progettino, ho utilizzato le librerie e il codice di Cyotek: http://cyotek.com/blog/convert-a-pdf-into-a-series-of-images-using-csharp-and-ghostscript

I passi da seguire sono abbastanza ben descritti. Li ripeto come memorandum:
- Download e install dei GhostScript http://www.ghostscript.com/ : copiare la DLL da copiare nella root dell'applicazione o nella bin.
- Download dei due progetti forniti da Cyotek: Cyotek.GhostScript (classe che espone i metodi di interfaccia con GS) e Cyoteck.GhostScript.PDFConversion (classe che espone i metodi di conversione).
- Estrarre i due progetti, compilarli e aggiungere le DLL nelle referenze del nostro progetto.
- E ora due righe di codice per utilizzarlo:

static System.Drawing.Bitmap ConvertPdfToImage(string sourcePdf)
{
    MemoryStream streamImage = new MemoryStream();
    //conversione for gs
    Pdf2ImageSettings settings;
    settings = new Pdf2ImageSettings();
    settings.AntiAliasMode = AntiAliasMode.High;
    settings.Dpi = 300;
    settings.GridFitMode = GridFitMode.Topological;
    settings.ImageFormat = ImageFormat.Jpeg;
    settings.TrimMode = PdfTrimMode.CropBox;

    System.Drawing.Bitmap img = new Pdf2Image(sourcePdf, settings).GetImage()
}

Bene.
I due metodi insieme mi hanno permesso di ottenere quanto cercavo.
E il cliente è contento. Spero!


Thursday, September 15, 2011

Getting Started on the Google+ API

E ora sarà dura battaglia a facebook!
Google apre le sue prime API per utilizzare la piattaforma Google+.
E' cosa dell'ultimissima ora, mi è appena arrivata comunicazione proprio da G!

Due i punti di partenza.
Un post su http://googleplusplatform.blogspot.com/, con descrizione delle prime (ridotte) funzionalità.
E il nuovo sito per gli sviluppatori per Google+ Platform: https://developers.google.com/+/

Ma veniamo a cosa possiamo fare, per adesso, con queste prime API.
Traduco, più o meno liberamente, alcuni passi base dal post ufficiale.

Let ' S Go Public!

Questo rilascio iniziale di API è focalizzato solo sui dati pubblici: consente di leggere le informazioni che le persone hanno condiviso pubblicamente su Google +.
Ad esempio, se si desidera ottenere le informazioni del mio profilo, è possibile utilizzare il metodo people.get inviando una richiesta HTTP, con l'id del mio utente:

GET https://www.googleapis.com/plus/v1/people/100661704413873131754?key=yourAPIKey

La riposta è un output JSON:

{
 "kind": "plus#person",
 "id": "100661704413873131754",
 "displayName": "Giorgio Guerrieri",
 "tagline": "Chi cerca trova",
 "gender": "male",
 "aboutMe": "",
 "url": "https://plus.google.com/100661704413873131754",
 "image": {
  "url": "https://lh6.googleusercontent.com/-Nx4ziEjmuQM/AAAAAAAAAAI/AAAAAAAAAAA/FVoLqNofkR8/photo.jpg"
 },
 "urls": [
  {
   "value": "http://giorgioguerrieri.blogspot.com"
  },
  {
   "value": "http://www.flickr.com/photos/gguerrieri/"
  },
  {
   "value": "http://www.facebook.com/giorgioguerrieri1977"
  },
  {
   "value": "http://twitter.com/ggorgos"
  },
  {
   "value": "http://www.flickr.com/people/gguerrieri/"
  },
  {
   "value": "https://plus.google.com/100661704413873131754",
   "type": "profile"
  },
  {
   "value": "https://www.googleapis.com/plus/v1/people/100661704413873131754",
   "type": "json"
  }
 ],
 "organizations": [
  {
   "name": "Liceo Scientifico Leonardo Da Vinci, Genova",
   "type": "school"
  },
  {
   "name": "CAP spa",
   "title": "Developer",
   "type": "work"
  }
 ],
 "placesLived": [
  {
   "value": "Genova",
   "primary": true
  }
 ]
}

Analogamente, è possibile ottenere un elenco dei post più recenti (immessi con visibilità Pubblica), utilizzando il metodo activities.list

GET https://www.googleapis.com/plus/v1/people/100661704413873131754/activities/public?key=yourAPIKey

Facile facile. Così sembra.

Ad ogni modo, non è molto diverso da quello che siamo abituati a fare con Google Maps, per esempio.
E, allo stesso modo, serve un API Key, che si può generare da questo sito https://code.google.com/apis/console/.

Interessante, infine, la sezione Download del sito https://developers.google.com/+/downloads che mette a disposizione progetti demo e sopratutto librerie per molti linguaggi comunemente utilizzati per il web.
(Manco a dirlo, la versione .NET è in beta, eh eh)

Beh, resisto alla tentazione di buttarmi su queste API e vado a nanna che è meglio!