2015年11月24日火曜日

PDF の外観辞書のレンダリング

PDF注釈→固定化する事案が発生しました。

Rect やら BBox やら Matrix やら出てきて、どれをどうするのか、良く分かりません。

得られた知見:

  • 外観辞書のXObjectの原点は、左下(0,0)原点で描いた方が後で管理しやすい。
  • 掛ける物。
    1. Matrix の行列
    2. Rectに合うように、BBoxの幅高さを拡大縮小する行列
    3. Rect のオフセットと、BBox のオフセットを足す行列
  • 外観辞書のXObjectは、同じスタンプを押す等で、使いまわしされます。

2015年11月16日月曜日

PDF の行列

[ a b c d e f ]
↑に対して、↓のようになります。
| a b |
| c d |
| e f |

座標を求めるときは、次のように:
            | a b |
| x y 1 | * | c d |
            | e f |

2015年9月30日水曜日

PDF の中身をオンデマンド修正する

ちょっと PDF ファイルをオンザフライで修正する案件が起こりました。理由は:

  • Subset だとフォント名が細工されるので、無理矢理訂正したい。
  • 四角形と円形の線が細くて、しかも半分隠れていた。RD を設定、Rect と Border を増加したい。 

その時のノウハウです。

基本的な流れとしては、
  • PdfReader で PDF を読み込みます。
  • 次に PdfReader がメモリー上に保持しているデータ構造を巡回して、必要個所を書き換えます。
  • その結果を PdfCopy で書き込んでもらう、という方針を取ります。。。

iText で大量の PDF をマージする
http://d.hatena.ne.jp/ocs/20110520/1305866658

PdfReader で読み込んだ後、copy.GetImportedPage(reader, i); と、copy.AddPage(page); で、コピーすれば良いようです。

参照が発生する場合は、AddToBody で追加した参照を、PdfReader から読んできたページの辞書にセット。

AP.Put(PdfName.N, copy.AddToBody(objN).IndirectReference);

PdfCopy を継承して細工します。CopyDictionary をオーバーライドすれば、事足りると思います。

  class AltPdfCopy : PdfCopy {
    public AltPdfCopy(Document document, Stream os)
      : base(document, os) {

    }

    protected override PdfDictionary CopyDictionary(PdfDictionary __inp, bool keepStruct, bool directRootKids) {
      var newd = base.CopyDictionary(__inp, keepStruct, directRootKids);
      ...
      return newd;
    }
  }

全部を晒すことはできませんが、フォント名の加工は次のようにしました。

  protected override PdfDictionary CopyDictionary(PdfDictionary __inp, bool keepStruct, bool directRootKids) {
    var newd = base.CopyDictionary(__inp, keepStruct, directRootKids);
    if (newd.GetAsName(PdfName.TYPE) == PdfName.FONTDESCRIPTOR) {
      PdfName FontName = newd.GetAsName(PdfName.FONTNAME);
      String a = enc.GetString(FontName.GetBytes());
      if (Regex.IsMatch(a, "/[A-Z]{6}\\+")) {
        newd.Put(PdfName.FONTNAME, new PdfName(enc.GetBytes("/" + a.Substring(1 + 6 + 1))));
        IsMod |= true;
      }
    }
    ...
  }

2015年9月29日火曜日

埋め込みフォントについて、Adobe Reader に警告を受ける

Adobe Reader に、埋め込みフォント…を抽出できません。一部の文字を正しくひょ宇治できない場合や、印刷できない場合があります、と警告を受けました。

PDFXplorer で見た場合、このような感じです。

PDF の仕様書によると、BaseFont はポストスクリプトフォント名を書かないといけないらしく、明らかに仕様に反した動きに思えます。

しかし、iTextSharp では、Subset フォントに対して、6文字のアルファベットとプラス記号を足すらしく。。。

目下、対策を検討中。

2015年9月19日土曜日

注釈の外観辞書を作成する

外観辞書とは「注釈の外観(見た目)を定義している辞書」とのことです。注釈の数だけ存在します。

具体的には /AP /N の辞書です。

仕様は、
Portable document format — Part 1 - Adobe
  12 Interactive Features
    12.5 Annotations
      12.5.5 Appearance Streams で詳しく触れられています。

PDFXplorer で確認すると、このようになっています:



サンプル PDF はこちら
 
この外観辞書で表現している外観です:


外観辞書がないとどうなる?
  • Adobe Reader などでは裏で作って表示するようです。印刷しても、きちんと印刷されます。
  • PDF 印刷専用ソフトなどでは、印刷されない場合有り ← これが問題

そこで、外観辞書を自作する必要がでてきます。

画像一枚をベタで描画する注釈のサンプルです:
http://itext.2136553.n4.nabble.com/Stamp-annotations-td2150068.html

/// <summary>
/// CreateStampAnnotation
/// </summary>
/// <remarks>
/// How to compute width and height in pt.:
/// 
/// ```cs
/// var w = image.Width * 72f / image.DpiX;
/// var h = image.Height * 72f / image.DpiY;
/// ```
/// 
/// How to use:
/// 
/// ```cs
/// pdfStamper.AddAnnotation(CreateStampAnnotation(...), 1);
/// ```
/// </remarks>
/// <param name="writer">pdfStamper.Writer or such.</param>
/// <param name="annotContents">The body part of annotation tooltip.</param>
/// <param name="stampHintName">Such as "Approved" suitable for file name.</param>
/// <param name="stampAuthor">The bolded headline part of annotation tooltip suitable for author or title.</param>
/// <param name="stampTitle">The invisible title can be confirmed at annotation property.</param>
public static PdfAnnotation CreateStampAnnotation(
    PdfWriter writer,
    string annotContents,
    string stampHintName,
    string stampAuthor,
    string stampTitle,
    iTextSharp.text.Image image,
    iTextSharp.text.Rectangle rect,
    int annotFlags = PdfAnnotation.FLAGS_PRINT
)
{
    var annot = PdfAnnotation.CreateStamp(writer, rect, annotContents, stampHintName);
    {
        {
            // "/N" is a normal appearance should be stored in appearance dictionary.
            var N = PdfAppearance.CreateAppearance(writer, image.Width, image.Height);
            image.SetAbsolutePosition(0, 0);
            N.AddImage(image);
            new PdfStream(N.ToPdf(N.PdfWriter));
            annot.SetAppearance(PdfName.N, N);
        }
        if (stampAuthor != null)
        {
            annot.Title = stampAuthor;
        }
        annot.Name = Guid.NewGuid().ToString();
        annot.Flags = annotFlags;
        if (stampTitle != null)
        {
            annot.Put(new PdfName("Subj"), new PdfString(stampTitle, PdfString.TEXT_UNICODE));
        }
    }
    return annot;
}


CreateAppearance と SetAppearance という物を初めて知りました。

2015年8月10日月曜日

文字列の回転

ColumnText.ShowTextAligned 等に指定する rotation の単位・方向を知らなかったので、テストして動作確認しました。

PDF なので、用紙は左下原点です。


2015年7月9日木曜日

注釈の外観辞書で、透明度を設定できない

ページの方のRESOURCES辞書に入ってしまうので、注釈のコンテンツからは参照できない模様。

注釈のRESOURCES辞書に入れないといけないが、、、そんな方法は用意されていないみたいで、自分で辞書を作って、コンテンツを作文して対応しました。

参考コード、例:
 var a = PdfAnnotation.CreateSquareCircle(wr1, pi.GetPDFRect(tx.Bounds), "", true);

 a.Put(PdfName.IC, pi.GetPdfArray(tx.Fore));//interior color
 a.Flags = PdfAnnotation.FLAGS_PRINT;
 {
  PdfDictionary objAP = new PdfDictionary();
  {
   PdfContentByte wr = new PdfContentByte(wr1);
   var bcFore = pi.GetBaseColor(tx.Fore);
   bcFore = new BaseColor(bcFore.R, bcFore.G, bcFore.B, (int)(255 * 0.3));

   var rc1 = pi.GetPDFRect(tx.Bounds);

   //PdfGState gs1 = new PdfGState();
   //gs1.FillOpacity = 0.3f;

   wr.SaveState();
   //wr.SetGState(gs1);
   wr.InternalBuffer.Append(" /GS1 gs ");
   wr.SetColorFill(bcFore);
   wr.SetLineWidth(0);
   wr.Rectangle(rc1.Left, rc1.Bottom, rc1.Width, rc1.Height);
   wr.Fill();
   wr.RestoreState();

   var bbox = pi.GetPDFRect(tx.Bounds);
   PdfStream objN = new PdfStream(wr.ToPdf(wr.PdfWriter));
   wr.Reset();
   objN.Put(PdfName.TYPE, PdfName.XOBJECT);
   objN.Put(PdfName.SUBTYPE, PdfName.FORM);
   objN.Put(PdfName.FORMTYPE, new PdfNumber(1));
   objN.Put(PdfName.BBOX, new PdfArray(new float[] { bbox.Left, bbox.Bottom, bbox.Right, bbox.Top }));
   {
    PdfDictionary objRes = new PdfDictionary();
    {
     PdfArray objProcSet = new PdfArray();
     {
      objProcSet.Add(PdfName.PDF);
     }
     objRes.Put(PdfName.PROCSET, objProcSet);
    }
    {
     PdfDictionary objExtGState = new PdfDictionary();
     {
      PdfDictionary objGS1 = new PdfDictionary();
      objGS1.Put(PdfName.ca, new PdfNumber(0.3f));

      objExtGState.Put(new PdfName("GS1"), objGS1);
     }
     objRes.Put(PdfName.EXTGSTATE, objExtGState);
    }
    objN.Put(PdfName.RESOURCES, objRes);
   }
   objAP.Put(PdfName.N, wr1.AddToBody(objN).IndirectReference);

  }
  a.Flags = PdfAnnotation.FLAGS_PRINT;
  a.Put(PdfName.AP, objAP);
  a.Put(PdfName.CA, new PdfNumber(0.3f));
 }
 wr1.AddAnnotation(a);

FreeTextCalloutのRD

PDF注釈の「引き出し線ツール」

回転ができます。これがややこしい。。。

RDは、回転した後の矩形で指定します。

[   ←  ↓   →   ↑   ]

+-----------------+
|        | 4      |
|        V        |
|   +---------+ 3 |
|-->|         |<--|
| 1 +---------+   |
|        A        |
|      2 |        |
+-----------------+

[ 1 2 3 4 ] の順で、DeflateRectする量を指定。

アノーテーションの例:

iTextSharp.text.Rectangle 座標系

左下が原点
右が+x
上が+y

new iTextSharp.text.Rectangle(llx, lly, urx, ury);

new iTextSharp.text.Rectangle(左下x, 左下y, 右上x, 右上y);

+y

A
|
|     +------------B
|     |            |
|     |            |
|     |            |
|     A------------+
|
0-------------------------> +x
A = (llx, llyB = (urx, ury)


(lower left X, lower left Y) - (upper right X, upper right Y)


単位はポイント pt です。1pt0.352777m参考

A4 の場合、約 595x842 pt ⇔ 210x297 mm

2015年7月7日火曜日

寸法・座標系の件

↓A3横(Windowsビットマップ)

(0, 0)
+--------------+ → +x
|              |
| A3 Landscape |
|              |
+--------------+ (3309, 2339)
↓ +y

↓A3横(PDF)

(0, 842.357361)

↑ +y
+--------------+ (1191.68909, 842.357361)
|              |
| A3 Landscape |
|              |
+--------------+ → +x (1191.68909, 0)
(0, 0)

rectangle について
[ llx lly urx ury ]
[ lower-left x, lower-left y, upper-right x, upper-right-y ]
[ left bottom right top ]


2015年7月6日月曜日

Graphics operators

PDF32000_2008.pdf より、一部抜き取り。


General graphics state
wSet the line width
JSet the line cap style
jSet the line join style
MSet the miter limit
dSet the line dash pattern (dashArray dashPhase)
riSet the colur rendering intent
iSet the flatness tolerance
gsSet the specified parameters (dictName)
gs Graphics state parameter dictionary
LWThe line width
LCThe line cap style
LJThe line joint style
MLThe miter limit
DThe line dash pattern
RIThe name of the rendering intent.
OPwhether to apply overprint; both or only stroking
opwhether to apply overprint for painting operations
OPMThe overprint mode
FontAn array of the form [font size]
BGThe black-generation function
BG2Same as BG, function or name
UCRThe undercolor-removal function
UCR2Same as UCR, function or name
TRThe transfer function
TR2Same as TR, function, array, or name
HTThe halftone dictionary or stream
FLThe flatness tolerance
SMThe smoothness tolerance
SAapply automatic stroke adjustment
BMThe current blend mode
SMaskThe current soft mask
CAThe current stroking alpha constant
caSame as CA; non-stroking operationg
AISThe alpha source flag
TKThe text knockout flag
Special graphics state
qSave the current graphics state
QRestore the graphics state
cmModify the current transformation matrix (CTM) by concatenating the specified matrix (a b c d e f)
Path construction
mBegin a new subpath (x y)
lAppend a straight line segment (x y)
cAppend a cubic Bezier curve (x1 y1 x2 y2 x3 y3)
vAppend a cubic Bezier curve (x2 y2 x3 y3)
yAppend a cubic Bezier curve (x1 y1 x3 y3)
hClose the current subpath
reAppend a rectangle (x y width height)
Path painting
SStroke the path
sClose and stroke the path
fFill the path
FEquivalent to f; compatibility
f*Fill the path
BFill and then stroke the path; winding
B*Fill and then stroke the path; even-odd
bClose, fill and then stroke the path; winding
b*Close, fill and then stroke the path; even-odd
nEnd the path object
Clipping paths
WModify yhe current clipping path; winding
W*Modify the current clipping path; even-odd
Text objects
BTBegin a text object
ETEnd a text object
Text state
TcSet the character spacing (charSpace)
TwSet the word spacing (wordSpace)
TzSet the horz scaling (scale)
TLSet the text leading (leading)
TfSet the text font (font size)
TrSet the rendering mode (render)
TsSet the text rise (rise)
Text positioning
TdMove to the start of the next line (tx ty)
TDMove to the start of the next line rel (tx ty)
TmSet the text matrix (a b c d e f)
T*Move to the start of the next line
Text showing
TjShow a text string (string)
TJShow one or more text strings (array)
'Move to the next line and show a text string (string)
"Move to the next line and show a text string (aw ac string)
Type 3 fonts
d0Set width info for the glyph (wx wy)
d1Set width and bbox info for the glypj (wx wy llx lly urx ury)
Color
CSSet the current colour space (name)
csSame as CS; non-stroking operations
SCSet the colour to use for stroking (c1...cn)
SCNSame as SC; supports Pattern,Separation,DeviceN and ICCBased (c1...cn; c1...cn name)
scSame as SC; for non-stroking operations (c1...cn)
scnsame as SCN; for non-stroking operations (c1...cn; c1...cn names)
GSet the stroking colour space to DeviceGray (gray)
gSame as G; for non-stroking operations (gray)
RGSet the stroking colour space to DeviceRGB (r g b)
rgSame as RG; for non-stroking operations
KSet the stroking colour space to DeviceCMYK (c m y k)
kSame as K; for non-stroking operations
Shading patterns
shPaint the shape and colour shading (name)
Inline images
BIBegin an inline image object
IDBegin the image data for an inline image object
EIEnd an inline image object
XObjects
DoInvoke named XObject (name)
Marked content
MPDesginate a marked-content point (tag)
DPDesignate a marked-content point (tag properties)
BMCBegin a marked-content sequence (tag)
BDCBegin a marked-content sequence (tag properties)
EMCEnd a marked-content sequence
Compatibility
BXBegin compatibility section
EXEnd compatibility section

PDFファイルを作成する

プログラムからPDFファイルを作成。

注釈(アノーテーション)を表示しようと思ったら、外観辞書(AP辞書)なる物が必要。情報源

実装方法として、既存のPDFに、注釈だけを後付けする方法。iTextSharp

細かい部分のドキュメントが十分ではないので、PDF32000_2008.pdfなどを調べます。

外観辞書のデバッグには、他のPDFのものとも比べたいはず。PDFXplorerなどを使います。