aboutsummaryrefslogtreecommitdiffstats
path: root/src/documentation/content/xdocs/components/slideshow/ppt-file-format.xml
blob: 202df1d436c513ccb5d57b664c93dce3343c6590 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
<?xml version="1.0" encoding="UTF-8"?>
<!--
   ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   ====================================================================
-->
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "document-v20.dtd">

<document>
    <header>
        <title>POI-HSLF - A Guide to the PowerPoint File Format</title>
        <subtitle>Overview</subtitle>
        <authors>
            <person name="Nick Burch" email="nick at torchbox dot com"/>
            <person name="Yegor Kozlov" email="yegor at dinom dot ru"/>
        </authors>
    </header>

    <body>
        <section><title>Records, Containers and Atoms</title>
		<p>
		PowerPoint documents are made up of a tree of records. A record may
		contain either other records (in which case it is a Container),
		or data (in which case it's an Atom). A record can't hold both.
		</p>
		<p>
		PowerPoint documents don't have one overall container record. Instead,
		there are a number of different container records to be found at
		the top level.
		</p>
		<p>
		Any numbers or strings stored in the records are always stored in
		Little Endian format (least important bytes first). This is the case
		no matter what platform the file was written on - be that a 
		Little Endian or a Big Endian system.
		</p>
		<p>
		PowerPoint may have Escher (DDF) records embedded in it. These
		are always held as the children of a PPDrawing record (record
		type 1036). Escher records have the same format as PowerPoint
		records.
		</p>
		</section>
		
		<section><title>Record Headers</title>
		<p>
		All records, be they containers or atoms, have the same standard
		8 byte header. It is:
		</p>
		<ul><li>1/2 byte container flag</li>
		<li>1.5 byte option field</li>
		<li>2 byte record type</li>
		<li>4 byte record length</li></ul>
		<p>
		If the first byte of the header, BINARY_AND with 0x0f, is 0x0f,
		then the record is a container. Otherwise, it's an atom. The rest
		of the first two bytes are used to store the "options" for the
		record. Most commonly, this is used to indicate the version of
		the record, but the exact usage is record specific.
		</p>
		<p>
		The record type is a little endian number, which tells you what
		kind of record you're dealing with. Each different kind of record
		has its own value that gets stored here. PowerPoint records have
		a type that's normally less than 6000 (decimal). Escher records
		normally have a type between 0xF000 and 0xF1FF.
		</p>
		<p>
		The record length is another little endian number. For an atom,
		it's the size of the data part of the record, i.e. the length
		of the record <em>less</em> its 8 byte record header. For a
		container, it's the size of all the records that are children of
		this record. That means that the size of a container record is the
		length, plus 8 bytes for its record header.
		</p>
		</section>

		<section><title>CurrentUserAtom, UserEditAtom and PersistPtrIncrementalBlock</title>
		<p><strong>aka Records that care about the byte level position of other records</strong></p>
		<p>
		A small number of records contain byte level position offsets to other
		records. If you change the position of any records in the file, then
		there's a good chance that you will need to update some of these
		special records.
		</p>
		<p>
		First up, CurrentUserAtom. This is actually stored in a different
		OLE2 (POIFS) stream to the main PowerPoint document. It contains
		a few bits of information on who lasted edited the file. Most
		importantly, at byte 8 of its contents, it stores (as a 32 bit
		little endian number) the offset in the main stream to the most
		recent UserEditAtom.
		</p>
		<p>
		The UserEditAtom contains two byte level offsets (again as 32 bit
		little endian numbers). At byte 12 is the offset to the 
		PersistPtrIncrementalBlock associated with this UserEditAtom
		(each UserEditAtom has one and only one PersistPtrIncrementalBlock).
		At byte 8, there's the offset to the previous UserEditAtom. If this
		is 0, then you're at the first one.
		</p>
		<p>
		Every time you do a non full save in PowerPoint, it tacks on another
		UserEditAtom and another PersistPtrIncrementalBlock. The 
		CurrentUserAtom is updated to point to this new UserEditAtom, and the
		new UserEditAtom points back to the previous UserEditAtom. You then
		end up with a chain, starting from the CurrentUserAtom, linking
		back through all the UserEditAtoms, until you reach the first one
		from a full save.
		</p>
<source>
/-------------------------------\
| CurrentUserAtom (own stream)  |
|   OffsetToCurrentEdit = 10562 |==\
\-------------------------------/  |
                                   |
/==================================/
|                                         /-----------------------------------\
|                                         | PersistPtrIncrementalBlock @ 6144 |
|                                         \-----------------------------------/
|  /---------------------------------\                  |
|  | UserEditAtom @ 6176             |                  |
|  |   LastUserEditAtomOffset = 0    |                  |
|  |   PersistPointersOffset =  6144 |==================/
|  \---------------------------------/
|                 |                       /-----------------------------------\
|                 \====================\  | PersistPtrIncrementalBlock @ 8646 |
|                                      |  \-----------------------------------/
|  /---------------------------------\ |                |
|  | UserEditAtom @ 8674             | |                |
|  |   LastUserEditAtomOffset = 6176 |=/                |
|  |   PersistPointersOffset =  8646 |==================/
|  \---------------------------------/
|                 |                       /------------------------------------\
|                 \====================\  | PersistPtrIncrementalBlock @ 10538 |
|                                      |  \------------------------------------/
|  /---------------------------------\ |                |
\==| UserEditAtom @ 10562            | |                |
   |   LastUserEditAtomOffset = 8674 |=/                |
   |   PersistPointersOffset = 10538 |==================/
   \---------------------------------/
</source>
		<p>
		The PersistPtrIncrementalBlock contains byte offsets to all the
		Slides, Notes, Documents and MasterSlides in the file. The first
		PersistPtrIncrementalBlock will point to all the ones that
		were present the first time the file was saved. Subsequent 
		PersistPtrIncrementalBlocks will contain pointers to all the ones
		that were changed in that edit. To find the offset to a given
		sheet in the latest version, then start with the most recent
		PersistPtrIncrementalBlock. If this knows about the sheet, use the
		offset it has. If it doesn't, then work back through older
		PersistPtrIncrementalBlocks until you find one which does, and
		use that.
		</p>
		<p>
		Each PersistPtrIncrementalBlock can contain a number of entries
		blocks. Each block holds information on a sequence of sheets.
		Each block starts with a 32 bit little endian integer. Once read
		into memory, the lower 20 bits contain the starting number for the
		sequence of sheets to be described. The higher 12 bits contain
		the count of the number of sheets described. Following that is
		one 32 bit little endian integer for each sheet in the sequence, 
		the value being the offset to that sheet. If there is any data
		left after parsing a block, then it corresponds to the next block.
		</p>
<source>
hex on disk      decimal        description
-----------      -------        -----------
0000             0              No options
7217             6002           Record type is 6002
2000 0000        32             Length of data is 32 bytes
0100 5000        5242881        Count is 5 (12 highest bits)
                                Starting number is 1 (20 lowest bits)
0000 0000        0              Sheet (1+0)=1 starts at offset 0
900D 0000        3472           Sheet (1+1)=2 starts at offset 3472
E403 0000        996            Sheet (1+2)=3 starts at offset 996
9213 0000        5010           Sheet (1+3)=4 starts at offset 5010
BE15 0000        5566           Sheet (1+4)=5 starts at offset 5566
0900 1000        1048585        Count is 1 (12 highest bits)
                                Starting number is 9 (20 lowest bits)
4418 0000        6212           Sheet (9+0)=9 starts at offset 9212
</source>
		</section>

		<section><title>Paragraph and Text Styling</title>
		<p>
			There are quite a number of records that affect the styling
			of text, and a smaller number that are responsible for the
			styling of paragraphs.
		</p>
		<p>
			By default, a given set of text will inherit paragraph and text
			stylings from the appropriate master sheet. If anything differs
			from the master sheet, then appropriate styling records will
			follow the text record.
		</p>
		<p>
			<em>(We don't currently know enough about master sheet styling
			to write about it)</em>
		</p>
		<p>
			Normally, powerpoint will have one text record (TextBytesAtom
			or TextCharsAtom) for every paragraph, with a preceding
			TextHeaderAtom to describe what sort of paragraph it is.
			If any of the stylings differ from the master's, then a 
			StyleTextPropAtom will follow the text record. This contains
			the paragraph style information, and the styling information
			for each section of the text which has a different style.
			(More on StyleTextPropAtom later)
		</p>
		<p>
			For every font used, a FontEntityAtom must exist for that font.
			The FontEntityAtoms live inside a FontCollection record, and 
			there's one	of those inside Environment record inside the
			Document record. <em>(More on Fonts to be discovered)</em>
		</p>
		</section>

		<section><title>StyleTextPropAtom</title>
		<p>
			If the text or paragraph stylings for a given text record
			differ from those of the appropriate master, then there will
			be one of these records.
		</p>
		<p>
			This record is made up of two lists of lists. Firstly, 
			there's a list of paragraph stylings - each made up of the
			number of characters it applies two, followed by the matching
			styling elements. Following that is the equivalent for 
			character stylings.
		</p>
		<p>
			Each styling list (in either list) starts with the number 
			of characters it applies to, stored in a 2 byte little 
			endian number. If it is a paragraph styling, it will be
			followed by a 2 byte number (of unknown use). After this is
			a four byte number, which is a mask indicating which stylings
			will follow. You then have an entry for each of the stylings
			indicated in the mask. Finally, you move onto the next set
			of stylings. 
		</p>
		<p>
			Each styling has a specific mask flag to indicate its
			presence. (The list may be found towards the top of
			org.apache.poi.hslf.record.StyleTextPropAtom.java, and is
			too long to sensibly include here). For each styling entry
			will occur in the order of its mask value (so one with mask
			1 will come first, followed by the next highest mask value).
			Depending on the styling, it is either made up of a 2 byte
			or 4 byte numeric value. The meaning of the value will
			depend on the styling (eg for font.size, it is the font
			size in points).
		</p>
		<p>
			Some stylings are actually mask stylings. For these, the
			value will be a 4 byte number. This is then processed as 
			mask, to indicate a number of different sub-stylings. 
			The styling for bold/italic/underline is one such example.
		</p>
<source>
hex on disk      decimal        description
-----------      -------        -----------

0000             0              No options
A10F             4001           Record type is 4001
8000 0000        128            Length of data is 128 bytes
1E00 0000        30             The paragraph styling applies to 30 characters
0000             0              Paragraph options are 0
0018 0000        6144           0x0800=Text Alignment, 0x1000=Line Spacing
0000             0              Text Alignment = Left
5000             80             Line Spacing = 80

1C00 0000        28             The paragraph styling applies to 28 characters
0000             0              Paragraph options are 0
0010 0000        4096           0x1000=Line Spacing
5000             80             Line Spacing = 80

1900 0000        25             The paragraph styling applies to 25 characters
0000             0              Paragraph options are 0
0018 0000        6144           0x0800=Text Alignment, 0x1000=Line Spacing
0200             0              Text Alignment = Right
5000             80             Line Spacing = 80

6100 0000        61             The paragraph styling applies to 61 characters
                                (includes final CR)
0000             0              Paragraph options are 0
0018 0000        6144           0x0800=Text Alignment, 0x1000=Line Spacing
0000             0              Text Alignment = Left
5000             80             Line Spacing = 80

1E00 0000        30             The character styling applies to 30 characters
0100 0200        131073         0x0001=Char Props Mask, 0x20000=Font Size
0100             1              Char Props 0x0001=Bold
1400             20             Font Size = 20

1C00 0000        28             The character styling applies to 28 characters
0200 0600        393218         0x0002=Char Props Mask, 0x20000=Font Size,  0x40000=Font Color
0200             2              Char Props 0x0002=Italic
1400             20             Font Size = 20
0000 0005        83886080       Blue
 
1900 0000        25             The character styling applies to 25 characters
0000 0600        393216         0x20000=Font Size,  0x40000=Font Color
1400             20             Font Size = 20
FF33 00FE        4261426175     Red

6000 0000        96             The character styling applies to 96 characters
0400 0300        196612         0x0004=Char Props Mask, 0x10000=Font Index, 0x20000=Font Size
0400             4              Char Props 0x0004=Underlined
0100             1              Font Index = 1 (2nd Font in table)
1800             24             Font Size = 24
</source>
		</section>
		
		<section><title>Fonts in PowerPoint</title>
		<p>
			PowerPoint stores information about the fonts used in FontEntityAtoms,
			which live inside Document.Environment.FontCollection. For every different
			font used, a FontEntityAtom must exist for that font. There is always at 
			least one FontEntityAtom in Document.Environment.FontCollection,
			which describes the default font.
		</p>
		</section>
		
		<section><title>FontEntityAtom</title>
		<p>
			The instance field of the record header contains the zero based index of the
			font. Font index entries in StyleTextPropAtoms will refer to their required
			font via this index.
		</p>
		<p>
			The length of FontEntityAtoms is always 68 bytes. The first 64 bytes of
			it hold the typeface name of the font to be used. This is stored as
			a null-terminated string, and encoded as little endian unicode. (The
			length of the string must not exceed 32 characters including the null
			termination, so the typeface name cannot exceed 31 characters).
		</p>
		
		<p>
			After the typeface name there are 4 bytes of bitmask flags. The details of these
			can be found in the Windows API, under the LOGFONT structure.
			The 65th byte is the output precision, which defines how closely the system chosen
			font must match the requested font, in terms of height, width, pitch etc.
			The 66th byte is the clipping precision, which defines how to clip characters
			that occur partly outside the clipping region.
			The 67th byte is the output quality, which defines how closely the system
			must match the logical font's attributes to those of the physical font used.
			The 68th (and final) byte is the pitch and family, which is used by the
			system when matching fonts.
		</p>
		</section>
	</body>
</document>