2011年4月30日土曜日

iTextで移動先の設定する方法

PDF間で特定の位置にリンクする場合、移動先名を設定しないとできません。

iTextで作成する場合、PdfDestinationで飛び先の位置を設定を予め作成したあとにそれぞれのページに追加します。 PdfContentByteもしくはPdfWriterのどちらかから追加できます。また一括でAddNamedDestinationsを使って一括に登録することが可能です。

次は新規文書に移動先を設定する例です。

using System;
using System.Drawing;
using iTextSharp.text;
using iTextSharp.text.pdf;
 
namespace ConsoleApplication1
{
    class Program
    {
        const float pt2mm = 72f / 25.4f;
 
        static void Main(string[] args)
        {
            string savePdf = "hogehoge.pdf";
            string loadPdf = "fugafuga.pdf";
 
            // 新規から作成
            string outputPath = @"hogehoge.pdf";
            Document pdoc = new Document(PageSize.A4);
            PdfWriter pw = PdfWriter.GetInstance(pdoc, new FileStream(outputPath, FileMode.Create));

            // 移動先の場所を設定
            PdfDestination dest = new PdfDestination(PdfDestination.XYZ, 105 * pt2mm, 148.5F * pt2mm, 0);
            
            // コンテンツの書き出し
            pdoc.Open();
            PdfContentByte pcb = pw.DirectContent;
            pcb.LocalDestination("name1", dest);        // 移動先名の作成
            Paragraph p = new Paragraph("Hello World");
            pdoc.Add(p);
            pcb.ClosePath();

            pdoc.NewPage();

            pw.AddNamedDestination("name2", 2, dest);   // 移動先名の作成
            pcb.ClosePath();
            pdoc.Close();
        }
    }
}

だが既存のPDFに追加する場合、新規から作成する場合と同じ設定を行ってもバグなのか分からないが追加することはできません。

仕方が無いので直接命令を書き込みます。
移動先の設定はCatalog→Names→Dests→Namesで取得できます。

PdfReader pdf = new PdfReader(@"hogehoge.pdf");
PdfDictionary root = pdf.Catalog;
PdfDictionary pDests = root.GetAsDict(PdfName.NAMES).GetAsDict(PdfName.DESTS);
PdfArray pDestNames = pDests.GetAsArray(PdfName.NAMES);
if (pDestNames == null)
{
    // 移動先の設定がない場合、作成する
    pDests.Put(PdfName.NAMES, new PdfArray());
    pDestNames = pDests.GetAsArray(PdfName.NAMES);
}

配列に移動先名、移動先の設定の順番に書き込みます。

PdfDestination dest = new PdfDestination(PdfDestination.XYZ, 0, 0, 0);
dest.AddPage(pdf.GetPageOrigRef(1));
pDestNames.Add(new PdfString("name1"));
pDestNames.Add(dest);

まとめると次のとおりになります。

using System;
using System.Drawing;
using iTextSharp.text;
using iTextSharp.text.pdf;
 
namespace ConsoleApplication1
{
    class Program
    {
        const float pt2mm = 72f / 25.4f;
 
        static void Main(string[] args)
        {
            string sourcePdf = @"hogehoge.pdf";
            string outputPath = @"fugafuga.pdf";

            PdfReader pdf = new PdfReader(sourcePdf);
            PdfStamper stamper = new PdfStamper(pdf, new FileStream(outputPath, FileMode.Create));

            // 既存PDFにあれこれ追加
            PdfContentByte cb = stamper.GetUnderContent(1);
            cb.BeginText();
            cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED), 18);
            cb.ShowTextAligned(Element.ALIGN_LEFT, "Sample", 30, 600, 0);
            cb.EndText();

            // 移動先の生成
            PdfDestination dest = new PdfDestination(PdfDestination.XYZ, 0, 0, 0);
            if (dest.HasPage())
                dest.ArrayList[0] = pdf.GetPageOrigRef(2);
            else
                dest.AddPage(pdf.GetPageOrigRef(2));

            // 移動先を設定
            PdfDictionary root = pdf.Catalog;
            PdfDictionary pDests = root.GetAsDict(PdfName.NAMES).GetAsDict(PdfName.DESTS);
            PdfArray pDestNames = pDests.GetAsArray(PdfName.NAMES);
            if (pDestNames == null)
            {
                pDests.Put(PdfName.NAMES, new PdfArray());
                pDestNames = pDests.GetAsArray(PdfName.NAMES);
            }
            pDestNames.Add(new PdfString("name"));
            pDestNames.Add(dest);

            stamper.Close();
            pdf.Close();
        }
    }
}

2011年4月24日日曜日

FDKサンプルからスクリプトに移植1

FM10のスクリプトはサンプルファイルがほとんど無く、リファレンスガイドすらない状態なので調査も兼ねてFDKサンプルのtextを移植してみました。
移植してみた雑感としては
  • 基本はFDKの仕様をオブジェクト化したという感じ(F_ObjHandleTの数値は各オブジェクトのidプロパティにある)
  • F_Printfに対応する命令がない?(今回はalertでごまかしてある)
  • alert(Javascript)とAlert(FrameMaker)とは違う
  • やっぱりポインタを気にすることないので非常に楽(特に文字列!)
XML読み書きのアプリケーションやDDE、COM、Win32APIを利用したいという場面以外ではFDKを使用するということはなくなるのではないだろうか。
var mMenu = app.GetNamedMenu("!MakerMainMenu");
var nMenu = mMenu.DefineAndAddMenu("APIMenu", "TextApi");

nMenu.DefineAndAddCommand(1, "GetTextCmd", "Get Text","");
nMenu.DefineAndAddCommand(2, "AddTextCmd","Insert Text","\\!GT");
nMenu.DefineAndAddCommand(3, "DeleteTextCmd", "Delete Text", "\\!IT");
nMenu.DefineAndAddCommand(4, "ColorTextCmd", "Color Text", "\\!DT");
nMenu.DefineAndAddCommand(5, "FontSizeCmd", "Change Font Size", "\\!CT");

UpdateMenus();

function Command(command)
{
    var doc = app.ActiveDoc;
    if(doc.id === 0)
        Alert("Please selected Document.", Constants.FF_ALERT_CONTINUE_WARN);

    switch(command)
    {
    /* Get text from selection. */
    case 1: 
        var tr = doc.TextSelection;
        
        if (tr.beg.obj.id === 0 || ((tr.beg.obj.id === tr.end.obj.id) && (tr.beg.offset === tr.end.offset)))
        {
            Alert("Please select some text and try again.", Constants.FF_ALERT_CONTINUE_WARN);
            break;
        }
        var textItems = doc.GetTextForRange(tr, Constants.FTI_String);
        alert(CreateStringFromTextItems(textItems));
        break;
        
    /* Add text. */
    case 2:
        var tr = doc.TextSelection;
        if (tr.beg.obj.id === 0)
        {
            Alert("Please select an insertion point and try again.", Constants.FF_ALERT_CONTINUE_WARN);
            break;
        }
        if (!((tr.beg.obj.id === tr.end.obj.id) && (tr.beg.offset === tr.end.offset)))
        {
            if (Alert("Do you wish to overwrite the selected text?", Constants.FF_ALERT_NO_DEFAULT))
                break;
        }
        doc.AddText(tr.beg, "The new CoffeeTool\011");
        break;

    /* Delete selected text. */
    case 3:
        var tr = doc.TextSelection;
        if (tr.beg.obj.id === 0 || ((tr.beg.obj.id === tr.end.obj.id) && (tr.beg.offset === tr.end.offset)))
        {
            Alert("Please select some text and try again.", Constants.FF_ALERT_CONTINUE_WARN);
            break;
        }

        doc.DeleteText(tr);
        break;

 /* Change the color of selected text to Red. */
 case 4:
        var tr = doc.TextSelection;
        if (tr.beg.obj.id === 0 || ((tr.beg.obj.id === tr.end.obj.id) && (tr.beg.offset === tr.end.offset)))
        {
            Alert("Please select some text and try again.", Constants.FF_ALERT_CONTINUE_WARN);
            break;
        }
        var color = doc.GetNamedObject(Constants.FO_Color, "Red");  // Color name is different in each language.
        var props = doc.GetTextProps(tr.beg);
        var i = GetPropIndex(props, Constants.FP_Color);
        props[i].propVal.obj = color;
        doc.SetTextProps(tr, props);
        break;

    /* Change the font size of the selected text to 30 points. */
    case 5:
        var tr = doc.TextSelection;
        if (tr.beg.obj.id === 0 || ((tr.beg.obj.id === tr.end.obj.id) && (tr.beg.offset === tr.end.offset)))
        {
            Alert("Please select some text and try again.", Constants.FF_ALERT_CONTINUE_WARN);
            break;
        }
        /* Allocate memory for the property list. */
        props = AllocatePropVals(1);
        /* Set up the properties. */
        props[0].propIdent.num = Constants.FP_FontSize;
        props[0].propVal.ival = 30 * 65536;
        props[0].propVal.valType = Constants.FT_Metric;
        /* Apply the property list to the text selection. */
        doc.SetTextProps(tr, props);
        break;
    }
}

/***************************************************************
* Create a string from an TextItems structure.
****************************************************************/
function CreateStringFromTextItems(textItems)
{
    var s = "";
    for (var i = 0; i < textItems.length; i++)
    {
        if (textItems[i].dataType === Constants.FTI_String)
        {
            s += textItems[i].sdata;
        }
    }
    
    return s;
}

2011年1月12日水曜日

FrameMaker 10体験版

FrameMaker 10の発表、そして出荷が始まったらしい。
昨年末に動画で登場していたときにはすでにマスターアップが完了していたということなのだろう。

アップグレード内容はS1000、DITA1.2対応、それに関連する機能の強化が主なんだろう。市販のCMS(SharePoint、Documentum)に対応といったところだ。

早速、体験版を入れてあれこれ触ってみて目についた新しい機能は

  • 文字列の背景色の追加(Wordの蛍光ペンみたいなの)
    ただし、段落にはかけられなかったり日本語OpenTypeだといまいちだったりと使いどころに悩むところ。
  • Adobe ExtendScriptに対応
    長年待ち望まれた機能が遂に!これでほとんどのFDKの煩わしい処理から開放される。
    ただし、ドキュメントが整理されていなかったり中のアプリケーション構造がFDKと大して変わらなかったりと慣れてないとかなり難しいかもしれません(特にテキスト周り)
  • 構造の比較
    気になるのでメモ
  • 前バージョンのバグのチェック
    日本語フォントでCMYK出力できるようになったか・・・

下は10分位で適当に作ったスクリプトです。選択した画像のアンカー枠の大きさを選択した画像の大きさに合わせるスクリプトです。

var docObj = app.ActiveDoc;
var selGfx = docObj.FirstSelectedGraphicInDoc;
var afrmObj = selGfx.FrameParent;

afrmObj.Width = selGfx.Width;
afrmObj.Height = selGfx.Height;
selGfx.LocX = 0;
selGfx.LocY = 0;

問題はバージョンアップできるのが8からということか。(TCS3だと7.xからできるが本体より高い)
まだ世の中で動いているシステムの大半が7.x以下ではないだろうか。

2011年1月8日土曜日

Wordで2バイト文字のチェック

Wordデータを英語環境に持って行くと2バイトフォントが使われている箇所を探せないかという話があったのでいい方法はないかな?

下のサンプルは少々強引だが・・・選択範囲に使用している文字のフォント名を取得してWin32APIを使用してフォントが2バイト(日中韓)フォントかの確認をしています。
当然OSに無いフォントがある場合は正常に動作しません。たぶん・・・。

簡単に説明するとCharacter.Font.Nameが実際に表示されているフォントでCharacter.Font.ASCIIが欧文フォント、Character.Font.NameFarEastが日中韓フォントのどれかになります。
他にもアラビア語用とかも選択できるみたいだがめんどくさいので割愛ということで。

Option Explicit
Private Const LF_FACESIZE       As Long = 32&
Private Const LF_FULLFACESIZE   As Long = 64&

Private Const DEVICE_FONTTYPE   As Long = &H2&
Private Const RASTER_FONTTYPE   As Long = &H1&
Private Const TRUETYPE_FONTTYPE As Long = &H4&

Private Type LOGFONT
    lfHeight As Long
    lfWidth As Long
    lfEscapement As Long
    lfOrientation As Long
    lfWeight As Long
    lfItalic As Byte
    lfUnderline As Byte
    lfStrikeOut As Byte
    lfCharSet As Byte
    lfOutPrecision As Byte
    lfClipPrecision As Byte
    lfQuality As Byte
    lfPitchAndFamily As Byte
    lfFaceName(LF_FACESIZE - 1&) As Byte
End Type

Type NEWTEXTMETRIC
    tmHeight As Long
    tmAscent As Long
    tmDescent As Long
    tmInternalLeading As Long
    tmExternalLeading As Long
    tmAveCharWidth As Long
    tmMaxCharWidth As Long
    tmWeight As Long
    tmOverhang As Long
    tmDigitizedAspectX As Long
    tmDigitizedAspectY As Long
    tmFirstChar As Byte
    tmLastChar As Byte
    tmDefaultChar As Byte
    tmBreakChar As Byte
    tmItalic As Byte
    tmUnderlined As Byte
    tmStruckOut As Byte
    tmPitchAndFamily As Byte
    tmCharSet As Byte
    ntmFlags As Long
    ntmSizeEM As Long
    ntmCellHeight As Long
    ntmAveWidth As Long
End Type

Private Const ANSI_CHARSET        As Byte = 0&
Private Const BALTIC_CHARSET      As Byte = 186&
Private Const CHINESEBIG5_CHARSET As Byte = 136&
Private Const DEFAULT_CHARSET     As Byte = 1&
Private Const EASTEUROPE_CHARSET  As Byte = 238&
Private Const GB2312_CHARSET      As Byte = 134&
Private Const GREEK_CHARSET       As Byte = 161&
Private Const HANGUL_CHARSET      As Byte = 129&
Private Const MAC_CHARSET         As Byte = 77&
Private Const OEM_CHARSET         As Byte = 255&
Private Const RUSSIAN_CHARSET     As Byte = 204&
Private Const SHIFTJIS_CHARSET    As Byte = 128&
Private Const SYMBOL_CHARSET      As Byte = 2&
Private Const TURKISH_CHARSET     As Byte = 162&
Private Const VIETNAMESE_CHARSET  As Byte = 163&

Private IsCJK As Boolean                    ' CJKフォントかの確認

Private Declare Function EnumFontFamiliesEx Lib "gdi32" Alias "EnumFontFamiliesExA" ( _
    ByVal hdc As Long, _
    lpLogFont As LOGFONT, _
    ByVal lpEnumFontProc As Long, _
    ByVal LParam As Long, _
    ByVal dw As Long _
) As Long

Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    Destination As Any, _
    Source As Any, _
    ByVal Length As Long _
)

Private Declare Function GetDC Lib "user32" ( _
    ByVal hwnd As Long _
) As Long

Public Sub CheckFarEastFont()
' フォントが日中韓フォントかの確認
    Dim wd_Selection As Word.Selection
    Dim i As Long
    
    Set wd_Selection = Application.Selection
    
    For i = 1 To wd_Selection.Characters.Count
        With wd_Selection.Characters(i)
            If .Font.Name = .Font.NameFarEast And .Font.Name <> "" Then
                Call FontCheck(.Font)
            End If
        End With
    Next
End Sub

Private Sub FontCheck(wd_Font As Word.Font)
' フォントが日中韓のエンコードを所持しているかの確認
    Dim bytBuf()    As Byte
    Dim udtLogFont  As LOGFONT
    
    bytBuf = StrConv(wd_Font.Name, vbFromUnicode)
    With udtLogFont
        .lfCharSet = DEFAULT_CHARSET
        Call MoveMemory(.lfFaceName(0), bytBuf(0), UBound(bytBuf) + 1&)
        .lfPitchAndFamily = 0&
    End With
    
    IsCJK = False
    Call EnumFontFamiliesEx(GetDC(0), udtLogFont, AddressOf EnumFontFamProc, ByVal 0&, 0)
    If IsCJK Then
        wd_Font.Animation = wdAnimationMarchingRedAnts    ' CJKフォントの場合赤い線のアニメーション
    Else
        wd_Font.Animation = wdAnimationNone
    End If
End Sub

Private Function EnumFontFamProc(lpNLF As LOGFONT, lpNTM As NEWTEXTMETRIC, ByVal FontType As Long, LParam As Long) As Long
    Dim FaceName As String
    
    FaceName = StrConv(lpNLF.lfFaceName, vbUnicode)
    Debug.Print Left$(FaceName, InStr(FaceName, vbNullChar) - 1)    ' フォント名
    EnumFontFamProc = 1
    Select Case lpNLF.lfCharSet
    Case SHIFTJIS_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET, HANGUL_CHARSET:
        IsCJK = True
    End Select
End Function

しかし、Unicodeの時代なのに未だに2バイト文字というのもなんだかなぁ。

2011年1月5日水曜日

今年もいいことがありますように

忙しくて、ずうっと放置プレイだった。
まぁ、今年起きそうなことを予想という名の落書きを。

電子書籍バブルの崩壊

半年以内で起きるだろう・・・たぶん。
正直言って今の作り方ではいたずらに体力を消耗しているだけで何も生み出さないだろう。

この状況に誰もが電子書籍は本当に必要なのだろうかと思うだろう。

だが、ホリエモンが月840円のメルマガを1万人に売っているわけで・・・内容は別としてテキストのみの商品が何人もの人間が苦労して創り上げた電子書籍より売上も制作費の安さが勝っているわけです。
ほかにも同人誌でも1ヶ月で3万ダウンロードの売上を出したサークルもあるわけだから、電子書籍がダメという理由ではないと思う。

まぁ何が言いたいかというとあれこれ複雑な規格つくるよりhtmlやPDFでいいんじゃないという話。重要なのは中身だよ。

Andoroidの増殖

既定路線だろう。
今後もケータイからちょっとしたガジェット、カーナビなんかも。
いや・・・専用カーナビ不要論とかでてくるかも。
ただ、法人向けはWindows Phone 7かなとは思っているが日本でいつ出るのやら。

AppleStoreのブレーキ

アプリの量が膨大になって目的のアプリが見つかりづらいなどの不満が両者から公然と出てくるかも。
特に電子書籍型アプリとか、あと著作者に許諾を得ずに作成されて登録される海賊版アプリ。
何かしらの方策は打つとは思うけど。

Windows7の普及、XPの衰退

XPの販売終了、セキュリティの問題からWindows7に普及が加速するだろう。
問題は金のない中小企業がついていけるかというところ。

html5一色の1年

iPhone、Andoroid、PCとの共通の言語はとなるとJAVAではなく手軽さ、作成のしやすさからhtml5だろう。
そして、なんだかんだいってもInternetExplore 9のリリース。

 

あれこれ、書いてみたが今までのコンテンツのリファイン(焼き直し)という感じが強いかも。
要は声が大きい人が素晴らしいといえばみんな群がるという構造は今年も続くだろうという話。

2010年10月24日日曜日

FDKを使用してアラートの取得2

前回の続き

自作のFDKからマスターページの適用のfcodeを送っても、表示されるアラートを消すことができない。
どうやらnotificationが送られていないのが原因らしい。

結局はhWndを取得してキーコードを送るしかない。

2010年10月6日水曜日

FDKを使用してアラートの取得

FrameMaker Developer's Forumで面白い投稿があったのでメモ。
画像が表示できませんやマスターページを適用しますかといったアラートをFDKで取得し、自動ではいを押すプログラムです。
ここで使用されているFP_ActiveAlertはリファレンスにも載っていない実験的なプロパティみたいです。

#include "fapi.h"
#include "fdetypes.h"
#include "futils.h"

VoidT F_ApiInitialize(IntT init)
{
    if (init == FA_Init_First)
    {
        F_ApiNotification(FA_Note_Alert, True);
        F_ApiBailOut();
    }
}

VoidT F_ApiNotify(IntT notification, F_ObjHandleT docId, StringT filename, IntT iparm)
{
    F_ObjHandleT alertId;
    IntT uniq;

    switch (notification)
    {
    case FA_Note_Alert:
        // Get the id of the alert.
        alertId = F_ApiGetId(0, FV_SessionId, FP_ActiveAlert);
        uniq = F_ApiGetInt(FV_SessionId, alertId, FP_Unique);

        // If the alert is "Cannot display some imported graphics...", supppress it.
        switch(uniq)
        {
        case 40086: //"Cannot display some imported graphics..."
        case 40090: //"Cannot display some imported graphics..."
        case 46001: //Suppress ApplyMasterpages message
            F_ApiReturnValue(FR_YesOperation);
            break ;
        case 0:
            //Unfortunately no ID for "The font information changed..."
            if (F_StrCmp(filename, (StringT)"The font information for your system has changed. This change may affect the format and output of your document(s)."))
                F_ApiReturnValue(FR_YesOperation);
            if (F_StrCmp(filename, (StringT)"Die Schriftinformation für Ihr System hat sich geändert. Diese Änderung kann sich auf Formatierung und Ausgabe Ihrer Dokumente auswirken."))
                F_ApiReturnValue(FR_YesOperation);
            break;
        default:
            break;
        }
        break;

    default:
        break;
    }
    F_ApiBailOut();
}