2012年12月14日金曜日

ExecutorService の使い方

ここで説明するのは、おそらくもっとも安全にマルチスレッドプログラムを書く方法です。

さらに同様の方法で簡単に拡張することで、複数のスレッドを効率よく使うスレッドプール (Thread Pool) を利用できますので、 ぜひ覚えておきたい方法です。

その方法とは、 ExecutorService を利用することです。

ExecutorService では、Java のマルチスレッド・プログラミングの基本 でみたように Thread オブジェクトをそのまま生では使いません。 プログラムの処理を小さなタスクに分割して、そのタスクを Executor によって実行すると考えます。

タスクの状態

Executor によって処理されるタスクの状態は次の四つです。

ExecutorService - タスクの状態

  1. created (作成された)
    サブミットされていない状態のタスクは created ステートです。
  2. submitted (サブミットされた)
    submit または execute メソッドでタスクをサブミットします。サブミットされても処理が開始しない場合は、通常ブロッキングキューに入ります。 FIFO で処理されます。

    submit された以降、メモリが割り当てられない等何らかの理由で処理がアボートする場合は、 RejectedExecutionException が発生します。

  3. started (処理が開始した)
  4. complted (完了した)

この状態は不可逆で、 created から completed へと順番に変わります。

スレッドプールを容易に使える ExecutorService

さて、スレッドプール (Thread Pool) について簡単に説明します。

スレッドプール

スレッドプールは通常 Web サーバーやデータベースサーバーなど、複数のタスクを同時に素早く処理しなければならない状況で利用されます。

複数のスレッドをあらかじめ作成して待機させておき、タスクが来たら待っているスレッドにタスクを割り当てて処理を開始させる仕組みをスレッドプールと言います。処理すべきタスクが到着してからモタモタとスレッドを起動するのではなく、スレッドをあらかじめ起動しておき、タスクが割り当てられたらすぐに処理を開始出来るようにする、ということです。

ゼロからこうした仕組みを実装するとなると、少々面倒くさいのですが、 Java では ExecutorService というものがあり、スレッドプールをあなたのプログラムで簡単に利用できます。

ExecutorService のコードサンプル

では、実際にスレッドプールを用いてタスクの処理を行う簡単な例を示します。

package com.keicode.java.test;    import java.util.concurrent.ExecutorService;  import java.util.concurrent.Executors;    public class ThreadTest4 {      public static void main(String[] args) {            System.out.println("Entering main");            ExecutorService exec = Executors.newFixedThreadPool(3);            try {        for(int i=0;i<10; i++){          exec.execute(new Runnable(){            public void run() {              try {                Thread.sleep(1 * 1000);                System.out.println("Hello! - "                   + Thread.currentThread().getId());              } catch (InterruptedException e) {                e.printStackTrace();              }            }          });        }                System.out.println("Sleeping..."           + Thread.currentThread().getId());        Thread.sleep(15 * 1000);              } catch(InterruptedException e) {        e.printStackTrace();      }        System.out.println("Exit main");          }    }  

実行結果は次のようになりました。

Entering main  Sleeping...1  Hello! - 8  Hello! - 10  Hello! - 9  Hello! - 10  Hello! - 8  Hello! - 9  Hello! - 10  Hello! - 9  Hello! - 8  Hello! - 10  Exit main  

ここではスレッドプール内に 3 つのスレッドを作成して、そこに 10 個のタスクを投げています。それぞれのタスクは、1 秒待ってから Hello! というメッセージとスレッド ID を出力します。

出力結果から、 ID が 8, 9, 10 という 3 つのスレッドがスレッドプール内で実行されたことがわかりますね。

java.util.concurrent.Executors.newFixedThreadPool というメソッドで、固定数のスレッドを持つスレッドプールを利用する ExecutorService を取得します。

ExecutorService の execute メソッドを呼ぶことで、タスクは submitted 状態になります。

ここでは一度に 10 個のタスクの実行 (execute) を要求していますが、スレッドが 3 個起動されています。このためまず 3 個のタスクが started になり、 残りの 7 個は submitted ステートのままキューの中で待ちます。

開始したタスクが終了したら、キューの中のタスクがスレッドに割り当てられて started 状態になります。

2012年12月12日水曜日

How to pirate Windows 8 Metro apps, bypass in-app purchases, and more

The principal engineer for Nokia's WP7 and WP8 devices has demonstrated, in rather frank detail, how to pirate Windows 8 Metro apps, how to bypass in-app purchases, and how to remove in-game ads. These hacks aren't exactly easy, but more worryingly they're not exactly hard either.

On his blog (Google cache), Justin Angel shows that turning a trial version of a Metro app into the full version — i.e. pirating an app — is scarily simple. It's just a matter of downloading a free, open-source tool, and then using it to change a Metro app's XML attribute from "Trial" to "Full." Likewise, a quick change to a XAML file can remove an app's ads.

Pirating a Windows 8 Metro app: Turning a Trial version into a Full version

Bypassing in-app purchases is a little trickier, involving some reverse engineering of some DLLs and and decryption of database files, but Angel still makes it look fairly easy. Angel gives himself one million credits in Soulcraft, an RPG game — something that would cost you over a thousand dollars, if you performed a legitimate in-app purchase. Angel also demonstrates a way to bypass in-app purchases in WinJS (Metro/JavaScript) apps, by injecting scripts into IE10 (the rendering engine for WinJS apps).

Pirating a Windows 8 Metro/WinJS app: Checking out the JavaScript source

Ultimately, all of these hacks represent ways of getting stuff for free. This is obviously bad news for developers, who probably don't realize that by allowing trial downloads they are opening themselves up to piracy. In-app ads and purchases are massive revenue streams for developers, and yet we now see that it's very easy to circumvent both.

You can protect these files with encryption — and indeed, some of them are — but that's no good if you have access to the code that performs the encryption. As Angel says, "We have the algorithm used for encryption, we have the hash key and we have the encrypted data. Once we have all of those it's pretty simple to decrypt anything." Angel notes that there are some security mechanisms in place that stopped him from directly editing app DLL and JS files, but, as we can see, that didn't stop him from pirating apps or bypassing in-app purchases.

It's easy to blame Microsoft for this, but really this is an issue that is intrinsic to all installed applications. The fact is, Windows 8 Metro apps are stored on your hard drive — and this means that you have access to the code and data. In general, every installed application is vulnerable to these kinds of attacks. Hex editors, save game editors, bypassing Adobe's 30-day trials by replacing DLL files, pirating Windows 8 apps — these are all just different incarnations of the same attack vectors.

The only real solution is to provide some kind of server-side sanity checking: You hack the software from Trial to Full — but when you log in, the server knows that you haven't bought the software, and so it reverts you back to Trial mode. You give yourself one million credits — but the server checks your purchase history, knows that you cheated, and so resets your credits back to zero. The problem with this route, of course, is that it requires you to be online — and you know how we feel about always-on DRM. Plus, it's very easy to disable server-side checks with a little Hosts file hacking.

In short, Windows 8 Metro apps have been hacked, and it's now just a matter of time until some enterprising developer creates a one-button tool that pirates trial apps, unlocks every in-app purchase, and removes in-app ads. There are certainly changes that Microsoft could make to shore up the security of Metro apps, but it would only delay the inevitable. Really, this is just a natural part of Windows 8′s evolution.

 

Nokia Engineer Shows How To Pirate Windows 8 Metro Apps, Bypass In-app Purchases

"The principal engineer for Nokia's WP7 and WP8 devices, Justin Angel, has demonstrated, in rather frank detail, how to pirate Windows 8 Metro apps, how to bypass in-app purchases, and how to remove in-game ads. These hacks aren't exactly easy, but more worryingly they're not exactly hard either. Angel shows that turning a trial version of a Metro app into the full version — i.e. pirating an app — is scarily simple.It's just a matter of downloading an open-source app and changing an XML attribute from 'Trial' to 'Full.' Likewise, a quick change to a XAML file can remove an app's ads. Bypassing in-app purchases is a little trickier, involving some reverse engineering of some DLLs and and decryption of database files, but Angel still makes it look fairly easy. Angel gives himself one million credits in Soulcraft, an RPG game — something that would cost you over a thousand dollars, if you performed a legitimate in-app purchase. Angel also demonstrates a way to bypass in-app purchases in WinJS (Metro/JavaScript) apps, by injecting scripts into IE10 (the rendering engine for WinJS apps). It's easy to blame Microsoft for this, but isn't this really an issue that is intrinsic to all installed applications? The fact is, Windows 8 Metro apps are stored on your hard drive — and this means that you have access to the code and data. Hex editors, save game editors, bypassing Adobe's 30-day trials by replacing DLL files, pirating Windows 8 apps — these are all just different incarnations of the same attack vectors."

2012年12月11日火曜日

SVGBasics

Text - Plain or Not-So-Plain
Fancy Uses for Text
Okay, so SVG isn't a great format for your word-processor to save files in (with the whole "no word wrap" thing). It is great for rendering shiny little baubles that have text in them or revolve around some highly stylized text.

Making text follow a curve or shape
The textPath element can be used in conjunction with the path element to make things interesting very quickly.


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 500 300" version = "1.1">
<desc>
Text example
</desc>
<defs>
<path id = "s3" d = "M 60 0 L 120 0 L 180 60 L 180 120 L 120 180 L 60 180 L 0 120 L 0 60" fill = "green" stroke = "black" stroke-width = "3"/>
</defs>
<g fill = "navy">
<text font-size = "20">
<textPath xlink:href = "#s3">
Most wordprocessors don't allow octagonal lines ;)
</textPath>
</text>
</g>
</svg>

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 500 300" version = "1.1">
<defs>
<!-- Start at (10,90) end at (200,70) use control point (100,15) -->
<!-- Continue from (200,70) end at (400,30) use control point (340,140) -->
<path id = "s3" d = "M 10,90 Q 100,15 200,70 Q 340,140 400,30"/>
</defs>
<g fill = "navy">
<text font-size = "20">
<textPath xlink:href = "#s3">
Wavy text is the gimmick for many years to come
</textPath>
</text>
<use x = "0" y = "0" xlink:href = "#s3" stroke = "black" fill = "none"/>
</g>
</svg>

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 500 300" version = "1.1">
<defs>
<!-- Start at (10,90) end at (200,70) use control point (100,15) -->
<!-- Continue from (200,70) end at (400,30) use control point (340,140) -->
<path id = "s3" d = "M 50,50 A 20,20 0 0 1 90,50 A 40,40 0 0 1 10,50 A 60,60 0 0 1 130,50"/>
</defs>
<g fill = "navy">
<text font-size = "14" font-family = "fantasy">
<textPath xlink:href = "#s3">
Text written around a spiral path is too hard to read!
</textPath>
</text>
<!-- use x="0" y="0" xlink:href="#s3" stroke="black" fill="none"/ -->
</g>
</svg>
Text fill and stroking
Text can be stroked and filled in with gradients, solid colours or patterns. In this way, rendered text is treated like other graphical elements.

Here's a gradient used to fill a rectangle then some text (courtesy of Robert Service).


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 600 200" version = "1.1">
<defs>
<rect id = "r1" width = "250" height = "50" stroke = "black" stroke-width = "1"/>
<linearGradient id = "g1" x = "0%" y = "100%">
<stop stop-color = "olivedrab" offset = "0%"/>
<stop stop-color = "peru" offset = "20%"/>
<stop stop-color = "goldenrod" offset = "40%"/>
<stop stop-color = "firebrick" offset = "60%"/>
<stop stop-color = "thistle" offset = "80%"/>
<stop stop-color = "sandybrown" offset = "100%"/>
</linearGradient>
</defs>
<g font-size = "40">
<use x = "0" y = "0" xlink:href = "#r1" fill = "url(#g1)"/>
<text font-size = "20" fill = "url(#g1)" stroke = "none">
<tspan x = "10" y = "80">
Have you wandered in the wilderness, the sagebrush desolation,
</tspan>
<tspan x = "10" y = "140">
The bunch-grass levels where the cattle graze?
</tspan>
</text>
</g>
</svg>
Here's that same gradient used to stroke text, this time there is no fill.


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 600 200" version = "1.1">
<defs>
<rect id = "r1" width = "250" height = "50" stroke = "black" stroke-width = "1"/>
<linearGradient id = "g1" x = "0%" y = "100%">
<stop stop-color = "olivedrab" offset = "0%"/>
<stop stop-color = "peru" offset = "20%"/>
<stop stop-color = "goldenrod" offset = "40%"/>
<stop stop-color = "firebrick" offset = "60%"/>
<stop stop-color = "thistle" offset = "80%"/>
<stop stop-color = "sandybrown" offset = "100%"/>
</linearGradient>
</defs>
<g font-size = "40">
<use x = "0" y = "0" xlink:href = "#r1" fill = "url(#g1)"/>
<text font-size = "20" stroke = "url(#g1)" fill = "none">
<tspan x = "10" y = "80">
Have you whistled bits of rag-time at the end of all creation,
</tspan>
<tspan x = "10" y = "140">
And learned to know the desert's little ways?
</tspan>
</text>
</g>
</svg>
The 'stroke-width' attribute adjusts the width of the line used to trace text. You can also control the shape of corners and ends of those lines with 'stroke-linejoin' and 'stroke-linecap' which are shown next.


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 600 200" version = "1.1">
<desc>
Gradient example
</desc>
<g>
<text font-size = "200" fill = "none" stroke = "red" stroke-width = "10">
<tspan x = "10" y = "120" stroke-linejoin = "bevel">
Aa
</tspan>
<tspan x = "340" y = "120" stroke-linejoin = "round">
Aa
</tspan>
</text>
<text font-size = "10" fill = "black" stroke = "none">
<tspan x = "50" y = "140">
bevel join
</tspan>
<tspan x = "380" y = "140">
round join
</tspan>
</text>
</g>
</svg>
Caveats
SVG doesn't do automatic word wrapping. Tools that implement SVG could handle the wrapping for the author, then make multiple one-line text elements out of it. This approach precludes the text being changed at the viewing end (for example, changing the font or replacing text with translated text). This is something that it appears may change with SVG version 1.2.
Another approach (mentioned in the spec) is using the foreignObject element to render text using XHTML or some other XML derivative that handles wrapped text. I'm pretty sure that means today that your SVG plugin has to either render that foreignObject (which means understanding XHTML) or run some other plugin in the space the foreignObject occupies.

Here's an example of the foreignObject method based on the one provided by the w3c.


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox = "0 0 200 200" version = "1.1">
<desc>
This example uses the 'switch' element to provide a fallback graphical representation of an paragraph, if XMHTML is not supported.
</desc>
<switch>
<!-- Process the embedded XHTML if the requiredExtensions attribute evaluates to true (i.e., the user agent supports XHTML embedded within SVG). -->
<foreignObject width = "100" height = "50" requiredExtensions = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- XHTML content goes here -->
<body>
<p>
This text is written inside a foreignObject tag in order to take advantage of XHTML automatic word-wrapping.
</p>
</body>
</foreignObject>
<!-- Else, process the following alternate SVG. Note that there are no testing attributes on the 'text' element. If no testing attributes are provided, it is as if there were testing attributes and they evaluated to true. -->
<text font-size = "10" font-family = "Verdana">
<tspan x = "10" y = "10">
The foreignObject is not displayed.
</tspan>
</text>
</switch>
</svg>
One more workable approach exists, it relies on using the tspan element to mark a relative position for successive lines of text. This one is about the same as the multiple text element method, but one might be easier to adjust with a script than the other.

豪警察、Appleの地図を使用しないように呼びかける

オーストラリア・ビクトリア州ミルデューラの警察は、Appleの地図で国立公園に迷い込む事件が続発していることから、地図が修正されるまで利用しないように呼びかけているそうだ。

現在、iOSの地図でミルデューラ市を探すとマレー・サンセット国立公園の中央あたりに表示される。実際の場所からは70キロメートルほど離れた場所だ。国立公園内では気温が摂氏46度に達することもあり、飲料水や食料も確保できない。警察に保護されたドライバーの中には、国立公園内を24時間さまよった人もいるとのこと。警察では地図が修正されない限り命の危険もあるとして、旅行者には他の地図サービスを利用するよう強く推奨している。

Semaphore

計数セマフォーです。概念的に、セマフォーはパーミットのセットを維持します。各 acquire() は許可が利用可能になるまで必要に応じてブロックし、利用可能になったらパーミットを取得します。各 release() はパーミットを追加し、場合によってはブロックしている取得側を解放します。ただし、実際のパーミットオブジェクトは使用されません。Semaphore では、利用可能な数を数えて、それに応じた処理を行うだけです。

セマフォーは、物理的または論理的な一部のリソースにアクセス可能なスレッド数を制限するためによく使用されます。たとえば、次のクラスでは、セマフォーを使用して項目のプールへのアクセスを制御します。

class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}

public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}

// Not a particularly efficient data structure; just for demo

protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];

protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}

protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}

}


項目を取得する前に、各スレッドはセマフォーから、項目が使用可能であることを保証するパーミットを取得する必要があります。項目の処理が完了するとスレッドはプールに戻り、パーミットがセマフォーに返され、ほかのスレッドがその項目を取得できるようになります。acquire() の呼び出し時に同期ロックは保持されません。これは同期ロックにより、項目をプールに返すことができなくなるためです。セマフォーは、プールへのアクセスを制限する必要のある同期を、プール自体の一貫性を維持するために必要な同期からは分離してカプセル化します。

値を 1 に初期化されたセマフォーは、利用できるパーミットが最大で 1 個であるセマフォーとして使用されるため、相互排他ロックとして利用できます。これは一般には「2 進型セマフォー」と呼ばれます。利用可能なパーミットが 1 個か 0 個かの 2 つの状態しかないためです。このように使用される場合、多くの Lock 実装とは異なり、2 進型セマフォーは、「ロック」を所有者以外のスレッドで解放できるという特性を持ちます (セマフォーには所有権の概念がないため)。これは、デッドロックの回復のような特殊なコンテキストで便利です。

このクラスのコンストラクタは、オプションの「公平性」パラメータを受け入れます。false に設定すると、このクラスはスレッドがパーミットを取得する順序について保証しません。特に、「割り込み」(barging) が許可されています。つまり、acquire() を呼び出すスレッドに、待機していたスレッドよりも先にパーミットを割り当てることが可能です。論理的には、新しいスレッドが待機中のスレッドのキューの先頭に配置されることになります。公平性が true に設定されると、セマフォーは、acquire メソッドを呼び出すスレッドが、呼び出しが処理された順 (先入れ先出し、FIFO) でパーミットを取得するように選択されることを保証します。FIFO 順序付けは、必然的にこれらのメソッド内の特定の内部実行ポイントに適用されます。そのため、あるスレッドが別のスレッドより先に acquire を呼び出すが、そのスレッドよりあとに順序付けポイントに達する場合があります。また、メソッドからの復帰時にも同様のことが起こる可能性があります。また、時間指定のない tryAcquire メソッドは公平性設定を尊重せず、受け入れませんが、利用可能なパーミットは取得します。

通常、リソースアクセスを制御するために使用されるセマフォーは、リソースへのアクセスができないスレッドがないよう、公平に初期化される必要があります。ほかの種類の同期制御にセマフォーを使用する場合は、公平性を考慮するよりも不公平な順序付けによるスループットの利点のほうがしばしば重要になります。

このクラスには、同時に複数のパーミットを 取得 および 解放 するための簡易メソッドもあります。公平性を true に設定せずにこれらのメソッドを使用すると、無期限に延期される危険が増すことに注意してください。

メモリー整合性効果:release() などの「解放」メソッドを呼び出す前のスレッド内のアクションは、別のスレッドで acquire() などの正常終了した「取得」メソッドに続くアクションよりも「前に発生」します。

new Semaphore(0)

とあるコードの中に、以下のような記述がありました。

import java.util.concurrent.Semaphore;
...
Semaphore permit = new Semaphore(0);
これまで、ミューテックスは1つの、セマフォは任意の数のクリティカルセクションへの進入を許可するもの、と覚えていました。ですので、初期値に「0」が指定されたセマフォがどういった意味を持つのか理解できず調べてみました。その中で、意外にセマフォ自体を理解できていないことが分かり、認識の弱かった部分を纏めてみました。

所有(アカウント)という概念がない

ミューテックスの場合は獲得したタスク(スレッド)がそのミューテックスを所有しますが、セマフォには所有されるという概念がありません。以下のコードを実行し、スレッドダンプを取ってみました。

import java.util.concurrent.Semaphore;

import org.junit.Test;

public class SemaphoreTest {

@Test
public void trySemaphore() throws InterruptedException{
final Semaphore permit = new Semaphore(2);
Runnable task = new Runnable(){
public void run() {
try {
permit.acquire();
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
permit.release();
}
}
};

Thread t1 = new Thread(task, "t1");
Thread t2 = new Thread(task, "t2");
Thread t3 = new Thread(task, "t3");

t1.start();
t2.start();
t3.start();

t1.join();
t2.join();
t3.join();
}
}
上記コードの実行中にスレッドダンプを取ってみると、以下のようになります。

Full thread dump Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing):

"t3" prio=6 tid=0x046cfc00 nid=0xb00 waiting on condition [0x04c9f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x24b57978> (a java.util.concurrent.Semaphore$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
at java.util.concurrent.Semaphore.acquire(Unknown Source)
at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:15)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- None

"t2" prio=6 tid=0x046cf400 nid=0x308 waiting on condition [0x0489f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:16)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- None

"t1" prio=6 tid=0x046bf000 nid=0x860 waiting on condition [0x04c1f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:16)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- None
...
スレッドt1,t2はセマフォを獲得し、t3はセマフォを待っている状態ですが、「Locked ownable synchronizers:」は何れもNoneとなっています。

同じことをReentrantLockで試してみます。

Full thread dump Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing):

"t3" prio=6 tid=0x047e6800 nid=0xc2c waiting on condition [0x04aef000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(Unknown Source)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(Unknown Source)
at java.util.concurrent.locks.ReentrantLock.lock(Unknown Source)
at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:15)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- None

"t2" prio=6 tid=0x047e3400 nid=0x1204 waiting on condition [0x0466f000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(Unknown Source)
at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(Unknown Source)
at java.util.concurrent.locks.ReentrantLock.lock(Unknown Source)
at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:15)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- None

"t1" prio=6 tid=0x0478ec00 nid=0x5d4 waiting on condition [0x04c0f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:16)
at java.lang.Thread.run(Unknown Source)

Locked ownable synchronizers:
- <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
...
スレッドt1はロックを獲得しており、「Locked ownable synchronizers:」には獲得しているロックの情報が表示されます。セマフォは獲得したスレッドには関連せず、獲得できる数を管理しているだけです。

どのスレッドからも解放できる

セマフォは特定のスレッドと関連しない為、どのスレッドからでもセマフォを解放することができます。例えば以下のようなコード。

import java.util.Random;
import java.util.concurrent.Semaphore;

import org.junit.Test;

public class ConsumerProducer {

static class Value {
public Object value;
}

@Test
public void tryProvide() throws InterruptedException {
final Semaphore s = new Semaphore(0);
final Value shared = new Value();
final Random rand = new Random();

class Consumer implements Runnable {
public void run() {
while(true) {
try {
s.acquire();
System.out.println("get: " + shared.value);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

class Producer implements Runnable {
public void run() {
while(true) {
shared.value = rand.nextInt();
System.out.println("put: " + shared.value);
s.release();
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Thread t1 = new Thread(new Consumer());
t1.start();

Thread t2 = new Thread(new Producer());
t2.start();

t1.join();
t2.join();
}
}
Customerのスレッドはセマフォを獲得するだけ、Producerのスレッドはセマフォを解放するだけです。つまり、セマフォを獲得したスレッドとは異なるスレッドでセマフォを解放しています。上記コードでは「new Semaphore(0);」としていて、最初にCustomerのスレッドがセマフォを獲得しようとするのですが、Producerが値を準備してセマフォを解放するまでセマフォを獲得することができません。

もちろんObject#wait、Object#notifyを使って同じようなことはできますが、synchronizedブロックを必要としないセマフォの方が書き方としてはスマートだと思います。但し、前記のとおりセマフォには所有という考え方が無い為、例えばセマフォの獲得で長い時間待たされた場合、どのスレッドがセマフォを解放できていないのかスレッドダンプで確認しようとしても、セマフォを獲得しているスレッドを特定することはできません。この点は注意が必要です。

java.util.concurrent.Semaphoreはバイナリセマフォにはならない?

同時に1つの処理だけが実行されるよう相互排他を実現したいが、処理の開始と終了が同じスレッドではない(処理終了は別スレッドでないと判定できない)ため、LockではなくSemaphoreを使うことにしました。

Java SE 6の日本語版Javadocには、以下のような記述があります。

値を 1 に初期化されたセマフォーは、利用できるパーミットが最大で 1 個であるセマフォーとして使用されるため、相互排他ロックとして利用できます。
そこで、許可数を1でセマフォを作成しました。

Semaphore binarySemaphore = new Semaphore(1);
デバッグのため、ログにこのセマフォをacquire/releaseするたびにavailablePermitsを出力していたところ、

binarySemaphore.release();
logger.trace("binarySemaphore permits={}", binarySemaphore.availablePermits());
その値が1より大きな(例:2, 3, ...)値がログに出ていました。

Semaphoreの実装を調べてみると、release()を呼び出すたびに、availablePermitsが1ずつ増えるようになっています。Semaphoreのコンストラクタで指定する値は、あくまで初期値であり、以降はrelease()のたびに1増え、tryAcquire()/acquire()のたびに1減る、という振る舞いをしています。

つまり、release()が何かの拍子に複数呼ばれると、相互排他(同時に1つだけ処理を実行)が成立しないということになってしまいます。

AbstractQueuedSynchronzierを使った実装

java.util.concurrent.Semaphoreクラスは、内部でAbstractQueuedSynchronzierを使って実装しています。AbstractQueuedSynchronzierは、以前Java読書会で課題図書となった次の書籍に書かれています。(が、読書会の折には理解できずにいました。あちこちを検索してみましたが、使い方が難しい・・・。)

Java並行処理プログラミング —その「基盤」と「最新API」を究める—

この書籍を再度読みかえしながら、同期化ステートとして0または1のどちらかしか取り得ないセマフォ(BinarySemaphore)をAbstractQueuedSynchronzierを使って、なんとなくこんな使い方でいいのかな、というレベルで実装してみました。ちゃんとしたテストはしていないので、正しいかどうかは分かりません。

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

public class BinarySemaphore {
private final Sync sync;

private static class Sync extends AbstractQueuedSynchronizer {
Sync() {
setState(1);
}

protected final int tryAcquireShared(int ignored) {
return compareAndSetState(1, 0) ? 1 : -1;
}
protected final boolean tryReleaseShared(int ignored) {
setState(1);
return true;
}
final int availablePermits() {
return getState();
}
}

public BinarySemaphore() {
sync = new Sync();
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean tryAcquire() {
return sync.tryAcquireShared(1) >= 0;
}
public void release() {
sync.releaseShared(1);
}
public int availablePermits() {
return sync.availablePermits();
}
}
AbstractQueuedSynchronizerは、テンプレートパターンを使用して、同期化処理の要件に合わせて利用者がサブクラスで獲得/解放などのメソッドを実装するようなクラスです。

今回実装するセマフォは、獲得/解放は「共有的(Shared)」、つまり、獲得したスレッドでなくても解放ができるものです。よって、AbstractQueuedSynchronizerのサブクラスで次のメソッドをオーバーライドします。

tryAcquireShared
tryReleaseShared
同期化ステートの変更は、このメソッドの実装において、AbstractQueuedSynchronizerクラスのsetState/getState/compareAndSetStateメソッドを適宜呼び出すことで行います。

バイナリセマフォでは、解放(tryReleaseShared)を呼べば必ず状態が1となるので単純にsetState(1)を呼びました。獲得は、compareAndSetStateで、現状が1で変更後が0となるときに成功(1をリターン)、それ以外は失敗(-1をリターン)します。

2012年12月10日月曜日

SvgToPdf

It's a small standalone application. You can use this example for inspiration, but please buy the book if there's something you don't understand about the example. Read Chapter 15 for more info.
Chapter 15: Page content and structure
Keywords for this example: Graphics2D, PdfTemplate, Scalable Vector Graphics
If you want this example to work, you need the following jars: iText.jar, batik-awt-util.jar, batik-bridge.jar, batik-css.jar, batik-dom.jar, batik-ext.jar, batik-gvt.jar, batik-parser.jar, batik-script.jar, batik-svg-dom.jar, batik-util.jar, batik-xml.jar, xerces_2_5_0.jar
Make sure you have the following fonts in directory c:/windows/fonts/: Webdings (webdings.ttf).
This example uses the following resources: foobarcity.svg, foobarstreets.svg.
This example is used as a helper class for: SvgLayers
part4.chapter15.SvgToPdf
If you compile and execute this example, you'll get the following result:
foobar.pdf
You can download the full source code of SvgToPdf, or read it here:


/*
* This class is part of the book "iText in Action - 2nd Edition"
* written by Bruno Lowagie (ISBN: 9781935182610)
* For more info, go to: http://itextpdf.com/examples/
* This example only works with the AGPL version of iText.
*/
package part4.chapter15;

import java.awt.Graphics2D;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.XMLResourceDescriptor;
import org.w3c.dom.svg.SVGDocument;

import com.itextpdf.awt.PdfGraphics2D;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class SvgToPdf {

/** The resulting PDF. */
public static final String RESULT = "results/part4/chapter15/foobar.pdf";
/** The map (shapes). */
public static final String CITY = "resources/xml/foobarcity.svg";
/** The map (text = street names in English). */
public static final String STREETS = "resources/xml/foobarstreets.svg";

/** The SVG document factory. */
protected SAXSVGDocumentFactory factory;
/** The SVG bridge context. */
protected BridgeContext ctx;
/** The GVT builder */
protected GVTBuilder builder;

/** Creates an SvgToPdf object. */
public SvgToPdf() {
String parser = XMLResourceDescriptor.getXMLParserClassName();
factory = new SAXSVGDocumentFactory(parser);

UserAgent userAgent = new UserAgentAdapter();
DocumentLoader loader = new DocumentLoader(userAgent);
ctx = new BridgeContext(userAgent, loader);
ctx.setDynamicState(BridgeContext.DYNAMIC);

builder = new GVTBuilder();
}

/**
* Draws an SVG file to a PdfTemplate.
* @param map the template to which the SVG has to be drawn.
* @param resource the SVG content.
* @throws IOException
*/
public void drawSvg(PdfTemplate map, String resource) throws IOException {
Graphics2D g2d = new PdfGraphics2D(map, 6000, 6000);
SVGDocument city = factory.createSVGDocument(new File(resource).toURL()
.toString());
GraphicsNode mapGraphics = builder.build(ctx, city);
mapGraphics.paint(g2d);
g2d.dispose();
}

/**
* Creates a PDF document.
* @param filename the path to the new PDF document
* @throws DocumentException
* @throws IOException
* @throws SQLException
*/
public void createPdf(String filename) throws IOException, DocumentException {
// step 1
Document document = new Document(new Rectangle(6000, 6000));
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
PdfTemplate map = cb.createTemplate(6000, 6000);
drawSvg(map, CITY);
cb.addTemplate(map, 0, 0);
PdfTemplate streets = cb.createTemplate(6000, 6000);
drawSvg(streets, STREETS);
cb.addTemplate(streets, 0, 0);
// step 5
document.close();
}

/**
* Main method.
*
* @param args no arguments needed
* @throws DocumentException
* @throws IOException
*/
public static void main(String[] args) throws IOException, DocumentException {
new SvgToPdf().createPdf(RESULT);
}
}

http://itextpdf.com/examples/iia.php?id=263

SvgLayers

It's a small standalone application. You can use this example for inspiration, but please buy the book if there's something you don't understand about the example. Read Chapter 15 for more info. Note that this example depends on SvgToPdf, and maybe some other examples.
Chapter 15: Page content and structure
Keywords for this example: Graphics2D, Optional Content Groups, PdfLayer, PdfTemplate, PdfContentByte > showTextAligned()
If you want this example to work, you need the following jars: iText.jar, batik-awt-util.jar, batik-bridge.jar, batik-css.jar, batik-dom.jar, batik-ext.jar, batik-gvt.jar, batik-parser.jar, batik-script.jar, batik-svg-dom.jar, batik-util.jar, batik-xml.jar, xerces_2_5_0.jar
Make sure you have the following fonts in directory c:/windows/fonts/: Webdings (webdings.ttf).
This example uses the following resources: foobarcity.svg, foobarstreets.svg, foobarrues.svg, foobarstraten.svg.
part4.chapter15.SvgLayers
If you compile and execute this example, you'll get the following result:
foobarcity.pdf
You can download the full source code of SvgLayers, or read it here:


/*
* This class is part of the book "iText in Action - 2nd Edition"
* written by Bruno Lowagie (ISBN: 9781935182610)
* For more info, go to: http://itextpdf.com/examples/
* This example only works with the AGPL version of iText.
*/
package part4.chapter15;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;


import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfLayer;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

public class SvgLayers extends SvgToPdf {

/** The resulting PDF. */
public static final String RESULT = "results/part4/chapter15/foobarcity.pdf";
/** The map. */
public static final String RUES = "resources/xml/foobarrues.svg";
/** The map. */
public static final String STRATEN = "resources/xml/foobarstraten.svg";


/**
* Creates a PDF document.
* @param filename the path to the new PDF document
* @throws DocumentException
* @throws IOException
* @throws SQLException
*/
public void createPdf(String filename) throws IOException, DocumentException {
// step 1
Document document = new Document(new Rectangle(6000, 6000));
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
writer.setViewerPreferences(PdfWriter.PageModeUseOC | PdfWriter.FitWindow);
writer.setPdfVersion(PdfWriter.VERSION_1_5);
// step 3
document.open();
// step 4
// CREATE GRID LAYER
PdfLayer gridLayer = new PdfLayer("Grid", writer);
gridLayer.setZoom(0.2f, 1);
gridLayer.setOnPanel(false);
// CREATE STREET LAYERS
PdfLayer streetlayer = PdfLayer.createTitle(
"Streets / Rues / Straten", writer);
PdfLayer streetlayer_en = new PdfLayer("English", writer);
streetlayer_en.setOn(true);
streetlayer_en.setLanguage("en", true);
PdfLayer streetlayer_fr = new PdfLayer("Fran\u00e7ais", writer);
streetlayer_fr.setOn(false);
streetlayer_fr.setLanguage("fr", false);
PdfLayer streetlayer_nl = new PdfLayer("Nederlands", writer);
streetlayer_nl.setOn(false);
streetlayer_nl.setLanguage("nl", false);
streetlayer.addChild(streetlayer_en);
streetlayer.addChild(streetlayer_fr);
streetlayer.addChild(streetlayer_nl);
ArrayList<PdfLayer> radio = new ArrayList<PdfLayer>();
radio.add(streetlayer_en);
radio.add(streetlayer_fr);
radio.add(streetlayer_nl);
writer.addOCGRadioGroup(radio);
// CREATE MAP
PdfContentByte cb = writer.getDirectContent();
PdfTemplate map = cb.createTemplate(6000, 6000);
// DRAW CITY
drawSvg(map, CITY);
cb.addTemplate(map, 0, 0);
PdfTemplate streets = cb.createTemplate(6000, 6000);
// DRAW STREETS
drawSvg(streets, STREETS);
streets.setLayer(streetlayer_en);
cb.addTemplate(streets, 0, 0);
PdfTemplate rues = cb.createTemplate(6000, 6000);
drawSvg(rues, RUES);
rues.setLayer(streetlayer_fr);
cb.addTemplate(rues, 0, 0);
PdfTemplate straten = cb.createTemplate(6000, 6000);
drawSvg(straten, STRATEN);
straten.setLayer(streetlayer_nl);
cb.addTemplate(straten, 0, 0);
// DRAW GRID
cb.saveState();
cb.beginLayer(gridLayer);
cb.setGrayStroke(0.7f);
cb.setLineWidth(2);
for (int i = 0; i < 8; i++) {
cb.moveTo(1250, 1500 + i * 500);
cb.lineTo(4750, 1500 + i * 500);
}
for (int i = 0; i < 8; i++) {
cb.moveTo(1250 + i * 500, 1500);
cb.lineTo(1250 + i * 500, 5000);
}
cb.stroke();
cb.endLayer();
cb.restoreState();

// CREATE INFO LAYERS
PdfLayer cityInfoLayer = new PdfLayer("Foobar Info", writer);
cityInfoLayer.setOn(false);
PdfLayer hotelLayer = new PdfLayer("Hotel", writer);
hotelLayer.setOn(false);
cityInfoLayer.addChild(hotelLayer);
PdfLayer parkingLayer = new PdfLayer("Parking", writer);
parkingLayer.setOn(false);
cityInfoLayer.addChild(parkingLayer);
PdfLayer businessLayer = new PdfLayer("Industry", writer);
businessLayer.setOn(false);
cityInfoLayer.addChild(businessLayer);
PdfLayer cultureLayer = PdfLayer.createTitle("Leisure and Culture",
writer);
PdfLayer goingoutLayer = new PdfLayer("Going out", writer);
goingoutLayer.setOn(false);
cultureLayer.addChild(goingoutLayer);
PdfLayer restoLayer = new PdfLayer("Restaurants", writer);
restoLayer.setOn(false);
goingoutLayer.addChild(restoLayer);
PdfLayer theatreLayer = new PdfLayer("(Movie) Theatres", writer);
theatreLayer.setOn(false);
goingoutLayer.addChild(theatreLayer);
PdfLayer monumentLayer = new PdfLayer("Museums and Monuments",
writer);
monumentLayer.setOn(false);
cultureLayer.addChild(monumentLayer);
PdfLayer sportsLayer = new PdfLayer("Sports", writer);
sportsLayer.setOn(false);
cultureLayer.addChild(sportsLayer);
// DRAW INFO
BaseFont font = BaseFont.createFont(
"c:/windows/fonts/webdings.ttf", BaseFont.WINANSI,
BaseFont.EMBEDDED);
cb.saveState();
cb.beginText();
cb.setRGBColorFill(0x00, 0x00, 0xFF);
cb.setFontAndSize(font, 36);
cb.beginLayer(cityInfoLayer);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x69), 2700, 3100, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x69), 3000, 2050, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x69), 3100, 2550, 0);
cb.beginLayer(hotelLayer);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2000, 1900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2100, 1950, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2200, 2200, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2700, 3000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2750, 3050, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2500, 3500, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2300, 2000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 3250, 2200, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 3300, 2300, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 3400, 3050, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 3250, 3200, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2750, 3800, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2900, 3800, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 3000, 2400, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2000, 2800, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe3), 2600, 3200, 0);
cb.endLayer(); // hotelLayer
cb.beginLayer(parkingLayer);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe8), 2400, 2000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe8), 2100, 2600, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe8), 3300, 2250, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe8), 3000, 3900, 0);
cb.endLayer(); // parkingLayer
cb.beginLayer(businessLayer);
cb.setRGBColorFill(0xC0, 0xC0, 0xC0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3050, 3600, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3200, 3900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3150, 3700, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3260, 3610, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3350, 3750, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3500, 4000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3500, 3800, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3450, 3700, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x46), 3450, 3600, 0);
cb.endLayer(); // businessLayer
cb.endLayer(); // cityInfoLayer
cb.beginLayer(goingoutLayer);
cb.beginLayer(restoLayer);
cb.setRGBColorFill(0xFF, 0x14, 0x93);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2650, 3500, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2400, 1900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2750, 3850, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2700, 3200, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2900, 3100, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2850, 3000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2800, 2900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 2300, 2900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 1950, 2650, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 1800, 2750, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 3350, 3150, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 3400, 3100, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xe4), 3250, 3450, 0);
cb.endLayer(); // restoLayer
cb.beginLayer(theatreLayer);
cb.setRGBColorFill(0xDC, 0x14, 0x3C);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xae), 2850, 3300, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xae), 3050, 2900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xae), 2650, 2900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xae), 2750, 2600, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xB8), 2800, 3350, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xB8), 2550, 2850, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xB8), 2850, 3300, 0);
cb.endLayer(); // theatreLayer
cb.endLayer(); // goingoutLayer
cb.beginLayer(monumentLayer);
cb.setRGBColorFill(0x00, 0x00, 0x00);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x47), 3250, 2750, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x47), 2750, 2900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x47), 2850, 3500, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xad), 2150, 3550, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xad), 3300, 2730, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xad), 2200, 2000, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xad), 2900, 3300, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0xad), 2080, 3000, 0);
cb.endLayer(); // monumentLayer
cb.beginLayer(sportsLayer);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x53), 2700, 4050, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x53), 2700, 3900, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x53), 2800, 3980, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x53), 1950, 2800, 0);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String
.valueOf((char) 0x53), 3700, 2450, 0);
cb.endLayer(); // sportsLayer
cb.endText();
cb.restoreState();
// step 5
document.close();
}

/**
* Main method.
*
* @param args no arguments needed
* @throws DocumentException
* @throws IOException
* @throws SQLException
*/
public static void main(String[] args) throws IOException, DocumentException {
new SvgLayers().createPdf(RESULT);
}
}

SvgLayers例

これは、 小規模なスタンドアロン·アプリケーションです。 あなたが例について理解していない何かがある場合は、インスピレーションを得るために、この例を使用していますが、本を購入してくださいすることができます。 記事を読む第15章詳細。 この例では、に依存することに注意してSvgToPdf 、およびおそらくいくつかの他の例を示します。
第15章:ページのコンテンツと構造
この例のためのキーワード: Graphics2Dの 、 オプショナルコンテンツグループ 、 PdfLayer 、 PdfTemplate 、 (PdfContentByte> showTextAligned)
この例では、操作したい場合は、次のものが必要です。
あなたは、ディレクトリ内の次のフォントがあることを確認しますc:/windows/fonts/ Webdings(webdings.ttfを):。
この例では、次のリソースを使用しfoobarcity.svg 、 foobarstreets.svg 、 foobarrues.svg 、 foobarstraten.svgを 。
part4.chapter15.SvgLayers
この例をコンパイルして実行する場合は、次の結果を取得します:
foobarcity.pdf
あなたの完全なソースコードをダウンロードすることができますSvgLayersのか、ここでそれを読む:

/ *
" -第2版アクションでiTextの" *このクラスは、ある本の一部である
ブルーノLowagie(:9781935182610 ISBN)によって書かれた*
http://itextpdf.com/examples/:*詳細については、に行く
*この例では、iTextのAGPLバージョンでのみ動作します。
* /
パッケージその4 第15章。

。。javaのIO FileOutputStreamを インポートします 。
。。javaのIO IOExceptionを インポートします 。
。。javaのutilのArrayListを インポートします 。


。。 インポート COM itextpdfテキスト ドキュメント ;
。。 インポート COM itextpdfテキスト DocumentException ;
。。 インポート COM itextpdfテキスト 四角形 ;
。。 インポート COM itextpdfテキストPDF BASEFONT ;
。。 インポート COM itextpdfテキストPDF PdfContentByte ;
。。 インポート COM itextpdfテキストPDF PdfLayer ;
。。 インポート COM itextpdfテキストPDF PdfTemplate ;
。。 インポート COM itextpdfテキストPDF PDFWriterは ;

パブリッククラス SvgLayersはSvgToPdfを{ 伸びる

/ **結果のPDF。 * /
public static finalの文字列の結果= "results/part4/chapter15/foobarcity.pdf";
/ mapを。** * /
public static finalの文字列RUES = "リソース/ XML / foobarrues.svg";
/ mapを。** * /
public static finalの文字列STRATEN = "リソース/ XML / foobarstraten.svg";


/ **
※PDF形式の文書を作成します。
新しいPDFドキュメントにする* @ paramファイル名パス
* @ DocumentExceptionをスロー
* @ IOExceptionをスロー
* @ SQLExceptionをスローします。
* /
公共 ボイド createPdf(文字列のファイル名)がIOExceptionをスローし DocumentExceptionを {
/ /ステップ1
文書ドキュメント= 新しい ドキュメント ( 新しい 四角形 (6000、6000));
/ /ステップ2
PDFWriterの作家= PDFWriterが 。 メソッドgetInstance(文書、 新しい FileOutputStreamを(結果));
作家setViewerPreferences( PDFWriterが PageModeUseOC |。 PDFWriterが FitWindow。);
作家setPdfVersion( PDFWriterが VERSION_1_5。);
/ /ステップ3
ドキュメント開く();
/ /ステップ4
/ /グリッド·レイヤーを作成する
PdfLayer gridLayer = 新しい PdfLayer ( "グリッド"、作家);
。gridLayer setZoom(0 0.2 F、1);
gridLayer setOnPanel(false)を指定します ;
/ /ストリートレイヤーを作成
PdfLayer streetlayer = PdfLayer 。createTitle(
"街/ Rues / Straten"、作家);
PdfLayer streetlayer_en = 新しい PdfLayer ( "英語"、作家);
。streetlayer_en シートン ( 真の );
。streetlayer_en setLanguage( "en"は 、 真の );
PdfLayer streetlayer_fr = 新しい PdfLayer ( "フラン\ U 00e7ais"、作家);
。streetlayer_fr シートン ( 偽 );
。streetlayer_fr setLanguage( "FR"、FALSE);
PdfLayer streetlayer_nl = 新しい PdfLayer ( "オランダ"、作家);
。streetlayer_nl シートン ( 偽 );
。streetlayer_nl setLanguage( "NL"、FALSE);
。streetlayer addChildメソッド(streetlayer_en);
。streetlayer addChildメソッド(streetlayer_fr);
。streetlayer addChildメソッド(streetlayer_nl);
ArrayListの<PdfLayer>ラジオ= 新しいArrayList <PdfLayer>();
ラジオ(streetlayer_en)を 追加します;
ラジオ(streetlayer_fr)を 追加します;
ラジオ(streetlayer_nl)を 追加します;
。作家addOCGRadioGroup(ラジオ);
/ /マップを作成
PdfContentByte CB =作家getDirectContent();
PdfTemplate 。マップ= CB createTemplate(6000、6000);
/ / DRAW CITY
drawSvg(地図、CITY);
。CB addTemplate(地図、0、0);
PdfTemplate 。通りは= CB createTemplate(6000、6000);
/ / DRAW STREETS
(道路、通り)drawSvg;
路上setLayer(streetlayer_en);
。CB addTemplate(通り、0、0);
PdfTemplate 。rues = CB createTemplate(6000、6000);
drawSvg(rues、RUES);
。rues setLayer(streetlayer_fr);
。CB addTemplate(rues、0、0);
PdfTemplate 。straten = CB createTemplate(6000、6000);
drawSvg(straten、STRATEN);
。straten setLayer(streetlayer_nl);
。CB addTemplate(straten、0、0);
/ /グリッドを描画
。CB セーブステート();
。CB beginLayer(gridLayer);
。CB setGrayStroke(0.7 F);
。CB setLineWidth(2);
するfor(int i = 0;のi <8は、i + +){
。CB のmoveTo(1250、1500 + I * 500);
。CB LINETO(4750、1500 + I * 500);
}
するfor(int i = 0;のi <8は、i + +){
CB のmoveTo(1250 + I * 500、1500);
CB LINETO(1250 + I * 500、5000);
}
CB ストローク();
。CB endLayer();
。CB restorestateを();

/ / INFOのレイヤーを作成
PdfLayer cityInfoLayer = 新しい PdfLayer ( "foobarの情報"、作家);
。cityInfoLayer シートン ( 偽 );
PdfLayer hotelLayer = 新しい PdfLayer ( "ホテル"、作家);
。hotelLayer シートン ( 偽 );
。cityInfoLayer addChildメソッド(hotelLayer);
PdfLayer parkingLayer = 新しい PdfLayer ( "駐車場"、作家);
。parkingLayer シートン ( 偽 );
。cityInfoLayer addChildメソッド(parkingLayer);
PdfLayer businessLayer = 新しい PdfLayer ( "産業"、作家);
。businessLayer シートン ( 偽 );
。cityInfoLayer addChildメソッド(businessLayer);
PdfLayer cultureLayer = PdfLayer 。createTitle( "レジャー·文化"、
作家);
PdfLayer goingoutLayer = 新しい PdfLayer ( "外出"、作家);
。goingoutLayer シートン ( 偽 );
。cultureLayer addChildメソッド(goingoutLayer);
PdfLayer restoLayer = 新しい PdfLayer ( "レストラン"、作家);
。restoLayer シートン ( 偽 );
。goingoutLayer addChildメソッド(restoLayer);
PdfLayer theatreLayer = 新しい PdfLayer ( "(動画)劇場"、作家);
。theatreLayer シートン ( 偽 );
。goingoutLayer addChildメソッド(theatreLayer);
PdfLayer monumentLayer = 新しい PdfLayer ( "美術館やモニュメント"
作家);
。monumentLayer シートン ( 偽 );
。cultureLayer addChildメソッド(monumentLayer);
PdfLayer sportsLayer = 新しい PdfLayer ( "スポーツ"、作家);
。sportsLayer シートン ( 偽 );
。cultureLayer addChildメソッド(sportsLayer);
/ /情報を描く
BASEFONTフォント= BASEFONT 。 のcreateFont(
"C :/ウィンドウ/フォント/ webdings.ttf"、 BASEFONT 。WINANSI、
BASEFONT 埋め込 ​​まれている)。
。CB セーブステート();
。CB beginText();
。CB setRGBColorFill(0x00は、0x00や0xFF)を ;
。CB setFontAndSize(フォント、36);
。CB beginLayer(cityInfoLayer);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x69の)、2700、3100、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x69の)、3000、2050、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x69の)、3100、2550、0);
。CB beginLayer(hotelLayer);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2000、1900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2100、1950、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2200、2200、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2700、3000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2750、3050、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2500、3500、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2300、2000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、3250、2200、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、3300、2300、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、3400、3050、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、3250、3200、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2750、3800、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2900、3800、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、3000、2400、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2000、2800、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe3)、2600、3200、0);
。CB endLayer(); / / hotelLayer
。CB beginLayer(parkingLayer);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe8)、2400、2000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe8)、2100、2600、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe8)、3300、2250、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xe8)、3000、3900、0);
。CB endLayer(); / / parkingLayer
。CB beginLayer(businessLayer);
。CB setRGBColorFill(が0xC0、0xC0に、が0xC0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3050、3600、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3200、3900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3150、3700、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3260、3610、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3350、3750、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3500、4000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3500、3800、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3450、3700、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0x46)、3450、3600、0);
。CB endLayer(); / / businessLayer
。CB endLayer(); / / cityInfoLayer
。CB beginLayer(goingoutLayer);
。CB beginLayer(restoLayer);
。CB setRGBColorFill(0xFFで、0x14を、0x93);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2650、3500、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2400、1900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2750、3850、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2700、3200、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2900、3100、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2850、3000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2800、2900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、2300、2900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、1950、2650、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、1800、2750、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、3350、3150、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、3400、3100、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)を辞書式の順序)、3250、3450、0);
。CB endLayer(); / / restoLayer
。CB beginLayer(theatreLayer);
。CB setRGBColorFill(0xDC、0x14で、その値を0x3c);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xAEの)、2850、3300、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xAEの)、3050、2900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xAEの)、2650、2900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xAEの)、2750、2600、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xB8)、2800、3350、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xB8)、2550、2850、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は0xB8)、2850、3300、0);
。CB endLayer(); / / theatreLayer
。CB endLayer(); / / goingoutLayer
。CB beginLayer(monumentLayer);
。CB setRGBColorFill(0x00は、0x00は、0x00を);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x47と)、3250、2750、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x47と)、2750、2900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0x47と)、2850、3500、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xADの)、2150、3550、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xADの)、3300、2730、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xADの)、2200、2000、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xADの)、2900、3300、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は 0xADの)、2080、3000、0);
。CB endLayer(); / / monumentLayer
。CB beginLayer(sportsLayer);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は $ 53)、2700、4050、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は $ 53)、2700、3900、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は $ 53)、2800、3980、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は $ 53)、1950、2800、0);
CB。showTextAligned( PdfContentByte 。ALIGN_CENTER、文字列
。valueOfの ((char)は $ 53)、3700、2450、0);
。CB endLayer(); / / sportsLayer
。CB endText();
。CB restorestateを();
/ /ステップ5
ドキュメント閉じる();
}

/ **
* Mainメソッド。
*
引数が必要ないする* @ param argsに
* @ DocumentExceptionをスロー
* @ IOExceptionをスロー
* @ SQLExceptionをスローします。
* /
公共の静的な無効メイン(文字列[] args)はIOExceptionをスローし DocumentExceptionを {
新しい SvgLayers()createPdf(結果)。
}
}