Мои два цента - NB это для уникального идентификатора устройства (err) - не для установки, как описано в блоге разработчиков разработчиков Android .
Следует отметить, что решение , предоставленное @emmby, возвращается в каждом идентификаторе приложения, поскольку SharedPreferences не синхронизированы между процессами (см. здесь здесь и здесь ). Поэтому я избегал этого вообще.
Вместо этого я инкапсулировал различные стратегии для получения идентификатора (устройства) в перечислении - изменение порядка констант перечисления влияет на приоритет различных способов получения идентификатора. Возвращается первый ненулевой идентификатор или генерируется исключение (согласно хорошей практике Java, не дающей значения null). Так, например, у меня сначала ТЕЛЕФОННЫЙ, но хорошим выбором по умолчанию будет бета-версия ANDROID_ID:
import android.Manifest.permission;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
// TODO : hash
public final class DeviceIdentifier {
private DeviceIdentifier() {}
/** @see http://code.google.com/p/android/issues/detail?id=10603 */
private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
+ "the Android ID bug - its ID is the emulator ID : "
+ IDs.BUGGY_ANDROID_ID;
private static volatile String uuid; // volatile needed - see EJ item 71
// need lazy initialization to get a context
/**
* Returns a unique identifier for this device. The first (in the order the
* enums constants as defined in the IDs enum) non null identifier is
* returned or a DeviceIDException is thrown. A DeviceIDException is also
* thrown if ignoreBuggyAndroidID is false and the device has the Android ID
* bug
*
* @param ctx
* an Android constant (to retrieve system services)
* @param ignoreBuggyAndroidID
* if false, on a device with the android ID bug, the buggy
* android ID is not returned instead a DeviceIDException is
* thrown
* @return a *device* ID - null is never returned, instead a
* DeviceIDException is thrown
* @throws DeviceIDException
* if none of the enum methods manages to return a device ID
*/
public static String getDeviceIdentifier(Context ctx,
boolean ignoreBuggyAndroidID) throws DeviceIDException {
String result = uuid;
if (result == null) {
synchronized (DeviceIdentifier.class) {
result = uuid;
if (result == null) {
for (IDs id : IDs.values()) {
try {
result = uuid = id.getId(ctx);
} catch (DeviceIDNotUniqueException e) {
if (!ignoreBuggyAndroidID)
throw new DeviceIDException(e);
}
if (result != null) return result;
}
throw new DeviceIDException();
}
}
}
return result;
}
private static enum IDs {
TELEPHONY_ID {
@Override
String getId(Context ctx) {
// TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
final TelephonyManager tm = (TelephonyManager) ctx
.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
w("Telephony Manager not available");
return null;
}
assertPermission(ctx, permission.READ_PHONE_STATE);
return tm.getDeviceId();
}
},
ANDROID_ID {
@Override
String getId(Context ctx) throws DeviceIDException {
// no permission needed !
final String andoidId = Secure.getString(
ctx.getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
if (BUGGY_ANDROID_ID.equals(andoidId)) {
e(ANDROID_ID_BUG_MSG);
throw new DeviceIDNotUniqueException();
}
return andoidId;
}
},
WIFI_MAC {
@Override
String getId(Context ctx) {
WifiManager wm = (WifiManager) ctx
.getSystemService(Context.WIFI_SERVICE);
if (wm == null) {
w("Wifi Manager not available");
return null;
}
assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess
// getMacAddress() has no java doc !!!
return wm.getConnectionInfo().getMacAddress();
}
},
BLUETOOTH_MAC {
@Override
String getId(Context ctx) {
BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
if (ba == null) {
w("Bluetooth Adapter not available");
return null;
}
assertPermission(ctx, permission.BLUETOOTH);
return ba.getAddress();
}
}
// TODO PSEUDO_ID
// http://www.pocketmagic.net/2011/02/android-unique-device-id/
;
static final String BUGGY_ANDROID_ID = "9774d56d682e549c";
private final static String TAG = IDs.class.getSimpleName();
abstract String getId(Context ctx) throws DeviceIDException;
private static void w(String msg) {
Log.w(TAG, msg);
}
private static void e(String msg) {
Log.e(TAG, msg);
}
}
private static void assertPermission(Context ctx, String perm) {
final int checkPermission = ctx.getPackageManager().checkPermission(
perm, ctx.getPackageName());
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Permission " + perm + " is required");
}
}
// =========================================================================
// Exceptions
// =========================================================================
public static class DeviceIDException extends Exception {
private static final long serialVersionUID = -8083699995384519417L;
private static final String NO_ANDROID_ID = "Could not retrieve a "
+ "device ID";
public DeviceIDException(Throwable throwable) {
super(NO_ANDROID_ID, throwable);
}
public DeviceIDException(String detailMessage) {
super(detailMessage);
}
public DeviceIDException() {
super(NO_ANDROID_ID);
}
}
public static final class DeviceIDNotUniqueException extends
DeviceIDException {
private static final long serialVersionUID = -8940090896069484955L;
public DeviceIDNotUniqueException() {
super(ANDROID_ID_BUG_MSG);
}
}
}
В принципе, у вас есть два варианта: либо вы создаете документ за один раз, либо создаете документ за два прохода.
Если вы создаете документ за один раз, вы не знаете, значение Y (общее количество страниц) заранее, поэтому вам нужно создать объект PdfTemplate
в качестве владельца места. Это показано в примере MovieCountries1 .
В этом примере мы создаем класс TableHeader
, который расширяет PdfPageEventHelper
. Мы создаем экземпляр класса PdfTemplate
для total
в методе OnOpenDocument()
, мы используем этот заполнитель total
в методе OnEndPage()
, где добавляем верхний или нижний колонтитул, и мы заполняем общее число страниц в методе OnCloseDocument()
.
Недостатком такого подхода является то, что его трудно предсказать размеры, необходимые для total
. Преимущество состоит в том, что вы можете создать документ за один раз (вам не нужно сначала создавать документ в памяти).
Если вы создаете документ за два прохода, вы создаете документ без заголовка / а затем вы просматриваете документ, чтобы узнать, сколько страниц он содержит. Затем вы используете PdfStamper
для добавления номеров страниц на каждую страницу. Это показано в примере TwoPasses .
Эти примеры взяты из моей книги «iText in Action - Second Edition». Вы можете загрузить главу 6 бесплатно с этого URL-адреса: http://manning.com/lowagie2/samplechapter6.pdf
Обратитесь к официальной документации [ ]] [4], когда вы сомневаетесь в конкретной функциональности.
Обновление: я не понимаю, почему вы предпочитаете смотреть на неофициальные примеры. Пример, который я вам дал, выглядит следующим образом:
using (PdfStamper stamper = new PdfStamper(reader, ms2)) {
// Loop over the pages and add a header to each page
int n = reader.NumberOfPages;
for (int i = 1; i <= n; i++) {
// Add content
}
}
Однако по какой-то причине вы Googled пример, который намного сложнее (и переполняет то, что вам нужно).
Просто замените // Add content
с:
ColumnText.ShowTextAligned(stamper.GetUnderContent(), Element.ALIGN_CENTER, new Phrase((i + 1) + "/" + totalPages, fontetexto), 297f, 15f, 0);
Обратите внимание, что я адаптировал значение x в методе ShowTextAligned()
. Вы создаете страницу размером A4, что означает, что ваша страница имеет 595 пользовательских единиц. Если вы добавите номера страниц в позиции x = 820, нижний колонтитул будет добавлен, но он будет находиться за пределами видимой области страницы. Пожалуйста, не копируйте и не вставляйте код, не зная параметров каждого метода.
Для записей - я использовал этот способ
byte[] bytes = memoryStream.ToArray();
//Save pdf in the temporary location.
System.IO.File.WriteAllBytes(Server.MapPath("~/TempReports/") + lbReports.Text + "_JEA.pdf", bytes);
/*This is a page counter - it stamps the number of pages in the document.
It will read dynamically the 'document' that was just closed above [document.Close();] from the location,
then in memory will write the new content plus the one from [byte[] bytes = memoryStream.ToArray();]
Solution has been applied from: https://www.aspsnippets.com/Articles/iTextSharp-Add-Page-numbers-to-existing-PDF-using-C-and-VBNet.aspx
*/
try
{
File.ReadAllBytes(Server.MapPath("~/TempReports/") + lbReports.Text + "_JEA.pdf");
iTextSharp.text.Font blackFont = FontFactory.GetFont("Arial", 7, iTextSharp.text.Font.BOLD, BaseColor.BLACK);
using (MemoryStream stream = new MemoryStream())
{
PdfReader reader = new PdfReader(bytes);
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
int pages = reader.NumberOfPages;
for (int i = 1; i <= pages; i++)
{
ColumnText.ShowTextAligned(stamper.GetUnderContent(i),
@Element.ALIGN_LEFT, new Phrase(lbReports.Text + " - HD - JEA", blackFont), 63f, 24f, 0);
ColumnText.ShowTextAligned(stamper.GetUnderContent(i),
@Element.ALIGN_CENTER, new Phrase("Page " + i.ToString() + " of " + pages, blackFont), 300f, 24f, 0);
ColumnText.ShowTextAligned(stamper.GetUnderContent(i),
@Element.ALIGN_RIGHT, new Phrase("" + DateTime.Now, blackFont), 549f, 24f, 0);
}
txConnection.Text = "This report contains " + pages + " page(s)";
}
bytes = stream.ToArray();
}//End of page counter
/*System.IO.File.WriteAllBytes will write all bytes to file again*/
System.IO.File.WriteAllBytes(Server.MapPath("~/TempReports/") + lbReports.Text + "_JEA.pdf", bytes);
// Temporary path that is used to display the pdf in the embed.
System.IO.File.WriteAllBytes(Server.MapPath("~/TempReports/ReportsEmbed/") + lbReports.Text + "_JEA.pdf", bytes);
/*this is what sends the PDF to the embed viewer object
The ltEmbed is what receives the plugin to dispplay the file*/
string embed = "<object data=\"{0}\" type=\"application/pdf\" width=\"698px\" height=\"450px\">";
embed += "If you are unable to view file, you can download it from <a href = \"{0}\">here</a>";
embed += " or download <a target = \"_blank\" href = \"http://get.adobe.com/reader/\">Adobe PDF Reader</a> to view it.";
embed += "</object>";
ltEmbed.Text = string.Format(embed, ("http://localhost:65423/TempReports/ReportsEmbed/") + lbReports.Text + "_JEA.pdf");
memoryStream.Close();
this.Context.ApplicationInstance.CompleteRequest();
}
catch (DocumentException exe)
{
txConnection.Text = "There has been an error generating the file. Please try again. Error: " + exe;
}