- John naplója
- HaT blog
- Schneier on Security
- Loloke
- Zee
- Lolokene (már megint titkos)
- PET Portál és Blog
- Bakai Balázs Java-közeli élményei
Az, hogy túl zöld. Mondhatni sötétzöld. De ne legyünk ilyen durvák, nézzük a tényeket, spekulációkat.
Az SPCR fórumára bevágott WD Support által írt levelek és SMART adatok szerint a WD GP winyók 8 másodperc inaktivitás után parkolják a fejet. A másfél éve létező SCPR fórum után az elmúlt egy évben valamikor felkerült a hivatalos Western Digital tudástárba is egy bejegyzés a problémáról (The S.M.A.R.T Attribute 193 Load/Unload counter keeps increasing on a SATA 2 hard drive).
A funkció előnye, hogy kevesebb áramot fogyaszt a merevlemez, ellenben növekszik a SMART Load_Cycle_Count (LCC) számláló értéke. A winyó adatlapja szerint ezekből a parkolásokból legalább 300 ezret ki kellene bírnia. Ha azonban pechesek vagyunk, és mondjuk pont 10 másodpercenként nyúlunk a lemezhez (vagy megteszi egy programunk 10 másodpercenként), akkor ezt a 300 ezres számot viszonylag hamar elérhetjük (kb. 35 nap folyamatos üzem esetén).
Persze ez nem jelenti azt, hogy a 300 001-edik parkolás már biztosan nem fog sikerülni, és utána a winyón tárolt adatok sosem lesznek elérhetőek, de akkor is idegesítő. Főleg, ha a parkolás hangját is halljuk. A fentebb említett fórumon egyébként van néhány felhasználó, akiknek több milliónál jár ez a számláló és a winyó hibátlanul működik.
De nézzük, hogy miért nem túl fair a helyzet:
Saját tapasztalat: Egy éves WD10EADS, 70 ezer körüli Load_Cycle_Count érték, tökéletes működés.
Frissítés, 2011. augusztus 12.:
A témáról a Prohardver is cikkezett nemrég. Ők is a WD egyik support oldalára hivatkoznak (Answer ID 5357), ami szerint lehet használni a wdidle3.exe-t Caviar Green lemezekkel. Pontosan idézve a support oldalt: „Set Idle3 to max time (effectively turns off load/unload power saving feature thus will use more power) per below link.”
A „below link”-et követve eljutunk az RE2GP Idle Mode Update Utility oldalára, ahonnan letölthető a wdidle3.exe egy figyelmeztetés mellett, mely szerint csak a WD1000FYPS-01ZKB0, WD7500AYPS-01ZKB0, WD7501AYPS-01ZKB0 modellekkel használható. Nem Raid Edition Green Power merevlemez nincs a listában. Az első lapon (Answer ID 5357) viszont Raid Edition merevlemez egyáltalán nincs említve.
Az anomáliáról kérdeztem a hivatalos supportot is, szerintük nem támogatott a WD10EADS, és a többi nem Raid Edition lemezek használata wdidle3.exe-vel: „Unfortunately this software was made for a specific model of internal drives and the Caviar Green GP drive is not one of them. It was made for RE3 drives.” (Igen, RE3-at írtak a levélben.) A support oldalt persze azóta sem tették egyértelművé, hiába kértem.
További adalék, hogy az eredeti, NGOHQ-n megjelent cikket a Western Digital PR is kommentálta, de a kommentben nyoma sincs tiltakozásnak, hogy a wdidle3.exe használata Caviar Green lemezekkel nem támogatott.
Ezek után nem könnyű eldönteni, hogy merjük-e a használni a wdidle3.exe-t, esetlegesen kockáztatva a garanciát, vagy inkább hagyjuk szépen növekedni az LCC-t.
Lentebb egy biztonsági mentést készítő szkript Sony PRS-650-hez. Amit tud: automatikus mount/umount, könyvtárlétrehozás dátum alapján, md5sum az elkészült mentésről, a database/media/audio/ alatti tartalmak kihagyása.
#!/bin/bash
### Sony PRS-650 backup script by palacsint, 2011. 07. 14.
### http://palacsint.hu/
INPUT_DIR=/mnt/prs650_internal
BACKUP_BASE_DIR=/backup/sony-prs-650
mkdir --parents $BACKUP_BASE_DIR || exit -1
BACKUP_DIR=$BACKUP_BASE_DIR/sony-prs-650-$(date +%Y-%m-%d)
echo "Backup directory: $BACKUP_DIR"
if [ -d $BACKUP_DIR ]; then
echo "Backup directory already exists, skip backup"
exit -1
fi
echo "Free space on backup drive: $(df -h $BACKUP_BASE_DIR | tail -1 | awk '{print $4}')"
MOUNTED=false
if mountpoint -q $INPUT_DIR; then
echo "Input directory $INPUT_DIR already mounted"
MOUNTED=true
else
mount $INPUT_DIR || exit -1
fi
mkdir --parents $BACKUP_DIR || exit -1
rsync --whole-file --recursive --filter='exclude, database/media/audio/**' $INPUT_DIR/ $BACKUP_DIR
find $INPUT_DIR | sed "s:$INPUT_DIR::" | sed 's:/::' > $BACKUP_DIR/file-list
cfv -q -C -t md5 -rr -p $BACKUP_DIR || exit -1
echo "Backup size: $(du -sh $BACKUP_DIR | awk '{print $1}')"
if [ "x$MOUNTED" = "xfalse" ]; then
umount $INPUT_DIR
sync
fi
echo "Backup OK"
Adott a következő könyvtár, amely napi mentéseket tartalmaz negyedévenként külön szülőkönyvtárban:
... backup-2011q1/backup-2011-03-31 backup-2011q1/backup-2011-04-01 backup-2011q2/backup-2011-04-02 ...
Az egyes napi mentések nem túl nagyok, de azért jó lenne egy idő után hatékonyan tömöríteni őket. Tart és gzipet vagy bzip2-t használva nem nyerünk sokat, a jelen esetben néhány száz megás, minimálisan eltérő napi mentések tömörített mérete csak 10-20 százalékkal kisebb, mint a könyvtárak tömörítetlen mérete.
A tar fájl a benne lévő fájlokat elérési útjuk szerinti ábécé sorrendben tárolja. Így a mindegyik napi mentésben előforduló azonos fájlok egymástól távol kerülnek a tar fájlban. A távolság miatt a gzip/bzip2 nem ismeri fel az ismétlődő mintákat, ezért nem tömörít hatékonyan. (Aki otthon van a témában, nyugodtan javítson ki, pontosítson kommentben.)
Gyors megoldás, ha gzip/bzip2 helyett 7-Zip-et használunk. Sajnos a gzip/bzip2 párost semmilyen paraméterrel nem sikerült rávennem, hogy maguktól felismerjék ezeket az ismétlődéseket, de a tar tartalmának megfelelő sorrendezésével segíthetünk a dolgon.
Erre két javasolt megoldás a credentiality blogon található. Az MD5 hash alapján történő sorrendezés megtalálható az előbbi linken, a fájlnév alapú sorrendezésre pedig egy példamegoldás a következő:
$ cat ~/bin/tar2
#!/bin/bash
export GZIP=-9
find "$1" -type f | awk '{split($0, pathElements, "/"); print pathElements[length(pathElements)] "\t" $0 }' | sort | cut -f 2- | tar czvf "$1.tar.gz" -T -
Sajnos a tar tartalmának megfelelő sorrendezése nem segít az ismétlődő, de túl nagy fájlok tömörítésén, ezen ismétlődéseket a gzip/bzip2 továbbra sem fogja felismerni. A 7-Zip használata erre is megoldást jelent.
Az Effective Java című könyvben is szerepel, hogy nem ajánlott nullt visszaadni, inkább részesítsük előnyben az üres tömböket és az üres kollekciókat, ha tömb vagy kollekció a visszatérési érték típusa. Lentebb arra látható egy példa, hogy JAXB használatakor hogyan kell annotálni az osztályunkat, hogy az unmarshall után a JAXB ne hagyjon maga után nullokat a kollekciók értékeként.
import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class Data {
@XmlElementWrapper(name = "list")
@XmlElement(name = "list-element")
private final List<String> list = new LinkedList<String>();
public Data() {
}
public List<String> getList() {
return new LinkedList<String>(list);
}
public void setList(final List<String> list) {
if (list == null) {
throw new IllegalArgumentException("list cannot be null");
}
this.list.clear();
this.list.addAll(list);
}
}
Érdemes megjegyezni, hogy ha a getter metódusra kerülne a @XmlElement annotáció, akkor a metódus által visszaadott listába kerülnének a JAXB által felvett elemek, emiatt a getterben nem lehetne másolatot visszaadni a belső listáról.
Dokumentum címének beállítása:
A szerző megadása:
A reset gomb frissíti az épp szerkesztett dokumentum szerző mezőjét a User Data alatt megadott adatokkal, megnyomása után az új szerző rögtön megjelenik a General fülön a Created címke mellett. A dokumentumot pdf-ként exportálva ezután már a beállított értékek fognak szerepelni a metaadatok között. Ne felejtsük el elmenteni az eredeti dokumentumot sem a metaadatok módosítása után.
A PDF toolkittel – vagy röviden csak pdftk-val – egyszerűen módosíthatóak a pdf fájlok metaadatai. A telepítés Debian alatt csak egy apt-get install pdftk, de van Windowsos bináris is, bár azt nem próbáltam.
A metaadatok közül a Sony PRS-650 esetében a szerző és a cím a legérdekesebb. A dátum szerinti sorrendezésnél a felmásolás dátuma számít, nem a pdf fájlban lévő időpont.
Metaadatok lekérdezése:
$ pdftk original.pdf dump_data > pdf.data
Az így létrejött pdf.data fájl tartalma valami ilyesmi:
InfoKey: Creator InfoValue: Writer InfoKey: Title InfoValue: árvíztűrő tükörfúrógép - ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP InfoKey: Producer InfoValue: OpenOffice.org 3.2 InfoKey: Author InfoValue: palacsint InfoKey: CreationDate InfoValue: D:20110727211139+02'00' PdfID0: d52f66ef5c592704640f4c44af42383 PdfID1: d52f66ef5c592704640f4c44af42383 NumberOfPages: 1
A címet az „InfoKey: Title” utáni sorban lévő InfoValue után kell megadni, a szerzőt pedig az „InfoKey: Author” utáni sorban. A cím jelen esetben az „árvíztűrő tükörfúrógép - ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP” szöveg. Értelemszerűen az ékezetes karaktereket a fenti formában kell megadni.
Végül a pdf.data módosítása után a metaadatok visszatöltése egy új fájlba:
$ pdftk original.pdf update_info pdf.data output metafix.pdf
A lenti EasyMock (3.0) tesztben átadunk az executornak egy Runnable példányt. Ez egy mock objektum, és talán azt várnánk, hogy a verify() majd jelzi, hogy az executor (egy másik szálban) engedély nélkül meghívta a mock objektum run() metódusát, azaz hibával elszáll a teszt.
import static org.junit.Assert.assertTrue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.easymock.EasyMock;
import org.junit.Test;
public class EasyMockTest {
@Test
public void testEasyMock() throws Exception {
final Runnable runnable = EasyMock.createMock(Runnable.class);
EasyMock.makeThreadSafe(runnable, true); // default true
EasyMock.replay(runnable);
runInNewThread(runnable);
EasyMock.verify(runnable); // should fail
}
private void runInNewThread(final Runnable runnable)
throws InterruptedException {
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(runnable);
executor.shutdown();
final boolean terminated = executor.awaitTermination(2,
TimeUnit.SECONDS);
assertTrue("terminated", terminated);
}
}
Nem ez történik, a teszt hiba nélkül lefut, a verify() nem jelez hibát. A konzolon viszont megjelenik a hibaüzenet:
Exception in thread "pool-1-thread-1" java.lang.AssertionError: Unexpected method call run(): at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45) at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:73) at $Proxy4.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)
JMock-kal (2.5.1) ugyanez a helyzet:
import static org.junit.Assert.assertTrue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(JMock.class)
public class JmockTest {
private Mockery mockery;
@Before
public void setUp() {
mockery = new Mockery();
}
@Test // should fail
public void testJmock() throws Exception {
final Runnable runnable = mockery.mock(Runnable.class);
runInNewThread(runnable);
}
...
}
A teszt sikeresen lefut, a hibaüzenet itt is csak a hibakonzolon jelenik meg.
Mockitót (1.8.5) használva jobbak az eredmények:
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class MockitoTest {
@Test
public void testMociko() throws Exception {
final Runnable runnable = mock(Runnable.class);
runInNewThread(runnable);
try {
verify(runnable, never()).run(); // failed
fail("verify");
} catch (final AssertionError expected) {
}
}
...
}
A verify() hívás hibát dob, ahogy vártuk. A konzolon pedig nem jelenik meg semmi, mert mock() metódus úgynevezett nice mock objektumokat készít, amelyek csak rögzítik a meghívott metódusaikat és sosem dobnak hibát.
Az EasyMock-hoz egy nem túl szép workaround saját ThreadFactory használata a kivételt elkapó UncaughtExceptionHandler-rel, majd a teszt végén az UncaughtExceptionHandler ellenőrzése.
import static org.junit.Assert.assertTrue;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.easymock.EasyMock;
import org.junit.Test;
public class EasyMockExceptionHandlerTest {
@Test
public void testEasyMockWithUncaughtExceptionHandler() throws Exception {
final Runnable runnable = EasyMock.createMock(Runnable.class);
EasyMock.makeThreadSafe(runnable, true); // default true
EasyMock.replay(runnable);
final AtomicBoolean threadError = new AtomicBoolean(false);
final ExecutorService executor = createExecutor(threadError);
executor.execute(runnable);
executor.shutdown();
final boolean terminated = executor.awaitTermination(2,
TimeUnit.SECONDS);
assertTrue("terminated", terminated);
EasyMock.verify(runnable);
assertTrue("thread error", threadError.get());
}
private ExecutorService createExecutor(final AtomicBoolean threadError) {
final ErrorFlagUncaughtExceptionHandler uncaughtExceptionHandler
= new ErrorFlagUncaughtExceptionHandler(threadError);
final ThreadFactory threadFactory = new ThreadFactory() {
final ThreadFactory defaultThreadFactory = Executors
.defaultThreadFactory();
public Thread newThread(final Runnable runnable) {
final Thread thread = defaultThreadFactory.newThread(runnable);
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
return thread;
}
};
final ExecutorService executor = Executors
.newSingleThreadExecutor(threadFactory);
return executor;
}
private class ErrorFlagUncaughtExceptionHandler implements
UncaughtExceptionHandler {
private final AtomicBoolean threadError;
public ErrorFlagUncaughtExceptionHandler(final AtomicBoolean threadError) {
this.threadError = threadError;
}
public void uncaughtException(final Thread thread,
final Throwable throwable) {
threadError.set(true);
}
}
}
JMock-hoz talán a 2.6-os vagy 2.7-es verzióban lesz erre megoldás, a jelenlegi dokumentáció alapján nem igazán támogatott a JMock mock objektumainak többszálú használata. Lásd még: JMOCK-213,
JMOCK-183.
EasyMock: During the replay phase, mocks are by default thread-safe.
Ugyanez a helyzet a Mockitónál is: You can let multiple threads call methods on a shared mock to test in concurrent conditions.
Az igazi megoldást azonban nem mock-olásra használt osztálykönyvtárban kellene implementálni, hanem a tesztelt kódban, hogy a más szálakon keletkező hibákról a hívó értesüljön, valamint a tesztünk is ezen az úton tudja meg, hogy hiba történt. Az EasyMock levelezőlistáján olvasható erről egy levél:
„Let's say the exception would be thrown by a real implementation, and *not* by EasyMock. How would your application know that the calculation went wrong?”
Erre használható például az ExecutorService:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.easymock.EasyMock;
import org.junit.Test;
public class ExecutorServiceTest {
@Test(expected = ExecutionException.class)
public void testEasyMockWithExecutorService() throws Exception {
final Runnable runnable = EasyMock.createMock(Runnable.class);
EasyMock.makeThreadSafe(runnable, true); // default true
EasyMock.replay(runnable);
final ExecutorService executorService = Executors
.newSingleThreadExecutor();
final Future> future = executorService.submit(runnable);
future.get();
}
}
Néhány tapasztalat egy spielmaterial.de csoportos rendelés kapcsán:
A múltkor lefagyás után nagyrészt sikerült visszaállítani az elveszett jegyzeteimet. Rögtön az újraindítás után kivettem a memóriakártyát, hogy ne rontsam tovább a helyzetet. Ezután a korábbi adatmentős írásomban ismertetett módon lementettem az egész kártya tartalmát későbbi elemzésre.
Az mentett képfájlt a phororec-nek adtam oda, elég sok fájl hozott vissza. Az eredeti fájlnév nem maradt meg, úgy kellett összevadászni az ömlesztett fájlokból a database/cache/cacheExt.xml és a Digital Editions gyökérkönyvtárban lévő, annot kiterjesztésű, egyébként szintén XML fájlokra hasonlító fájlokat. Az XML-ek gyökértag-jére érdemes keresni („<cacheExt” és „<annotationSet”). A két fájl közül a cacheExt.xml fájlba kerülnek olvasható módon a jegyzetek, de ez valószínűleg – ahogy a neve is mutatja – csak egy cache, a tényleges jegyzeteket az annot fájl tárolja.
A photorec a fájlok legutolsó változatán kívül elég sok régebbi változatot is kibányászott a memóriakártya eldugott szektoraiból. Ezek közül a legnagyobbakat választottam ki. A fájlok mérete arányos a benne lévő jegyzetek számával.
Rossz hír, hogy ezt a két fájlt szinkronban kell visszaállítani. Ez nekem nem akart összejönni. Az .annot fájl törlése után a cacheExt-ből is törlődtek az annotációk, viszont az annot fájl visszaállítása után nem generálódik újra a cache, hanem az annot fájl tartalma is törlődik. (Elég érdekes viselkedés egy cache-hez képest.) A kettő egy időben történő visszaállításakor pedig némelyik jegyzet nem volt kijelölve a szövegben, csak a jegyzetlistán jelent meg a hozzá tartozó bejegyzés.
Az annot fájlból és a könyvből kiindulva valószínűleg le lehetne generálni a cacheExt tartalmát is, ha máshogy nem is, a firmware forráskódja alapján biztosan, de ennyit nem ért meg a dolog. Ehelyett inkább kinyomtattam a legnagyobb cacheExt fájl releváns részeit és kézzel felvettem újra a jegyzeteket.
A cacheExt-ben ezek a részek az érdekesek:
<annotation date="Tue, 16 Nov 2010 22:10:42 GMT" name="..." page="1" pageOffset="1" pages="948" part="0" scale="2" synced="true" dpi="167" width="584" height="754" layoutVersion="1"> <start>...</start> <end>...</end> <comment></comment> </annotation>
A name attribútum tárolja a kijelölt szöveget, a page az oldalszámot (0 jelöli az első oldalt), a scale a kijelöléskori nagyítást (0 = S, 1 = M, 2 = L), a pageOffset pedig az adott fizikai oldalon belüli oldalt (nagyítás esetén van jelentősége).
A rajzokat külön SVG fájlok tárolják, az XML-ből csak hivatkozás van rájuk. A hivatkozás csak fájlnévvel történik, ami eléggé kellemetlen, mivel a photorec nem állítja vissza az eredeti fájlnevet.
A cacheExt-beli tag hasonló az előzőhöz:
<freehand date="Sun, 28 Nov 2010 18:16:31 GMT" name="..." page="785" pageOffset="0" pages="948" part="0" scale="0" synced="true" dpi="167" width="584" height="754" layoutVersion="1"> <svgFile width="584" height="754">1290968182066.77.svg</svgFile> <thumbFile width="115" height="149">1290968182066.77.jpg</thumbFile> <mark>...</mark> <comment></comment> </freehand>
A jpg fájl tartalmazza a rajz mögötti könyvoldal képét is, ami alapján találgatással össze lehet párosítani a photorec által megtalált képeket az XML freehand bejegyzéseivel. A könyvből ki kell keresni a page attribútumokban szereplő oldalakat, majd az ezekhez hasonlító képet. Ezek a képek nagyon kicsik (kb. 100 x 150 képpont), rossz minőségűek, de nekem sokat segítettek, mert esetemben az ábrák többnyire csak egy pipából álltak a már olvasott fejezetek elején, és ezek az oldalak változatosabb képet mutattak, mint a folytonos szöveg a fejezetek további oldalain. Érdemes figyelni (nem sorkizárt szöveg esetén) a sorok végére rajzolható görbét, a fejezetcím hosszát, ha eltérő, akkor a páros-páratlan oldalak közti fejléc, illetve lábléc különbségét, ábrák alakját.
A cacheExt fájlból a következő részlet az érdekes:
<text path="...pdf" preventDelete="true" opened="true"> <currentPosition date="Fri, 07 Jan 2011 13:32:25 GMT" name="undefined" page="552" pageOffset="0" pages="948" part="0" scale="0" synced="false" dpi="167" width="584" height="754" layoutVersion="1"> <mark>I3BkZmxvYyg5OWYwLDU1MikA</mark> <comment></comment> </currentPosition> ...
Itt is a korábban már ismertetett page, scale és a pageOffset attribútumokra kell figyelni.
Amire szükség lesz:
A fontok tar.gz-ben érkeznek, ami például a 7-Zippel kicsomagolható. A kitömörített könyvtárból a következő fájlokra lesz szükség:
Ízlés szerint persze lehet más fontokat is használni. A Linux Libertine fonttal a Sony PRS-650 esetében nem volt szerencsém, az eszköz nem kezelte megfelelően, néha kimaradtak bekezdések a könyvekből. A font lecserélése megoldotta a problémát. A Liberation fonttal még nem volt problémám, a Sony jól kezeli őket.
A Sigil telepítése után nyissuk meg az epub fájlt a File menü Open menüpontjával. A megnyitott fájl Book Browser ablakában a Fonts mappára kattintva jobbklikk, Add Existsing Items. Keressük meg a fenti négy ttf fájl, és adjuk hozzá a listához.
Ezután szintén Book Browser ablak, jobbklikk a Styles könyvtáron, Add New Item. Ha jól csináltuk, akkor létrejön egy Style0001.css nevű fájl. Duplaklikk a fájlnévre, majd másoljuk be az előző bejegyzésben is szereplő CSS tartalmat:
@font-face {
font-family: "LiberationSerif";
font-style: normal;
font-weight: normal;
src:url(../Fonts/LiberationSerif-Regular.ttf)
}
@font-face {
font-family: "LiberationSerif";
font-style: italic;
font-weight: normal;
src:url(../Fonts/LiberationSerif-Italic.ttf)
}
@font-face {
font-family: "LiberationSerif";
font-style: normal;
font-weight: bold;
src:url(../Fonts/LiberationSerif-Bold.ttf)
}
@font-face {
font-family: "LiberationSerif";
font-style: italic;
font-weight: bold;
src:url(../Fonts/LiberationSerif-BoldItalic.ttf)
}
body {
font-family: "LiberationSerif"
}
A CSS-ben az src:url kezdetű sorokban lévő fájlnévnek egyeznie kell a fenti listában szereplő fájlnevekkel. Ez alapesetben így van, ha mégsem működne valami, akkor ezt mindenképp ellenőrizzük, illetve ha más fontot használnánk, akkor figyeljünk rá.
Ha ezzel megvagyunk, akkor a Book Browser ablakban nyissuk meg az xhtml fájlokat, majd váltsunk át Code View-ra (View menü / Code View menüpont, vagy F11). A fájlok elején, a <head> és </head> közé helyezzük el a következő tag-et:
<link href="../Styles/Style0001.css" rel="stylesheet" type="text/css" />
Például:
<?xml version="1.0"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Liberation Serif Font Test</title> <link href="../Styles/Style0001.css" rel="stylesheet" type="text/css" /> </head> <body> <p>GYÜMÖLCSVÉDŐ ÁGYÚFŰNYÍRÓ</p> <p>Gyümölcsvédő ágyúfűnyíró</p> </body> </html>
A <head></head> között meglévő tartalom maradjon meg, azt ne töröljük.
Fontos, hogy beszúrt tag-ben a href után lévő fájlnévnek egyeznie kell a korábban létrehozott CSS nevével (Style0001.css), valamint a fenti link tag-nek minden xhtml fájlban szerepelnie kell.
Ha kész, akkor a Tools menü Validate Epub menüvel ellenőrizhető, hogy az eredmény megfelel-e az epub szabványnak. Jó tudni, hogy a validátor (jelenleg) sajnos nem ellenőrzi a CSS-ben lévő hivatkozásokat. Az ellenőrzést érdemes a módosítások megkezdése előtt is, hogy a forrásban lévő hibákkal ne itt találkozzunk először.
Végül innen letölthető egy mintaepub, ami tartalmazza a fentieket. Kérdés nyugodtan jöhet kommentben vagy e-mailben.
Legutóbbi hozzászólások
7 hét 10 óra
8 hét 1 nap
8 hét 1 nap
8 hét 1 nap
8 hét 6 nap
11 hét 6 nap
12 hét 5 nap
20 hét 6 nap
29 hét 6 nap
30 hét 6 nap