weasyprint 67.0__py3-none-any.whl → 68.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -301,7 +301,7 @@ def _out_of_flow_layout(context, box, index, child, new_children,
301
301
  return stop, resume_at, new_child, out_of_flow_resume_at
302
302
 
303
303
 
304
- def _break_line(context, box, line, new_children, next_lines, page_is_empty, index,
304
+ def _break_line(context, box, line, new_children, needed, page_is_empty, index,
305
305
  skip_stack, resume_at, absolute_boxes, fixed_boxes):
306
306
  """Break line where allowed by orphans and widows.
307
307
 
@@ -316,7 +316,6 @@ def _break_line(context, box, line, new_children, next_lines, page_is_empty, ind
316
316
  return True, False, resume_at
317
317
  # How many lines we need on the next page to satisfy widows
318
318
  # -1 for the current line.
319
- needed = max(box.style['widows'] - 1 - next_lines, 0)
320
319
  if needed > over_orphans and not page_is_empty:
321
320
  # Total number of lines < orphans + widows
322
321
  remove_placeholders(context, line.children, absolute_boxes, fixed_boxes)
@@ -377,15 +376,21 @@ def _linebox_layout(context, box, index, child, new_children, page_is_empty,
377
376
  # If we couldn’t break the line before but can break now, first try to
378
377
  # report footnotes and see if we don’t overflow.
379
378
  could_break_before = can_break_now = True
380
- next_lines = len(tuple(lines_iterator))
379
+ needed = box.style['widows'] - 1
380
+ for _ in lines_iterator:
381
+ needed -= 1
382
+ # Don’t iterate over all lines as it can be long.
383
+ if needed == -1:
384
+ break
381
385
  if len(new_children) + 1 < box.style['orphans']:
382
386
  can_break_now = False
383
- elif next_lines < box.style['widows']:
387
+ elif needed >= 0:
384
388
  can_break_now = False
385
389
  if len(new_children) < box.style['orphans']:
386
390
  could_break_before = False
387
- elif next_lines + 1 < box.style['widows']:
391
+ elif needed > 0:
388
392
  could_break_before = False
393
+ needed = max(0, needed)
389
394
  report = not context.in_column and can_break_now and not could_break_before
390
395
  reported_footnotes = 0
391
396
  while report and context.current_page_footnotes:
@@ -397,9 +402,8 @@ def _linebox_layout(context, box, index, child, new_children, page_is_empty,
397
402
  break
398
403
  else:
399
404
  abort, stop, resume_at = _break_line(
400
- context, box, line, new_children, next_lines,
401
- page_is_empty, index, skip_stack, resume_at, absolute_boxes,
402
- fixed_boxes)
405
+ context, box, line, new_children, needed, page_is_empty, index,
406
+ skip_stack, resume_at, absolute_boxes, fixed_boxes)
403
407
 
404
408
  # Revert reported footnotes, as they’ve been reported starting from the last
405
409
  # one.
@@ -414,8 +418,7 @@ def _linebox_layout(context, box, index, child, new_children, page_is_empty,
414
418
  # "When an unforced page break occurs here, both the adjoining
415
419
  # ‘margin-top’ and ‘margin-bottom’ are set to zero."
416
420
  # See issue #115.
417
- elif page_is_empty and context.overflows_page(
418
- bottom_space, new_position_y):
421
+ elif page_is_empty and context.overflows_page(bottom_space, new_position_y):
419
422
  # Remove the top border when a page is empty and the box is
420
423
  # too high to be drawn in one page
421
424
  new_position_y -= box.margin_top
@@ -433,8 +436,7 @@ def _linebox_layout(context, box, index, child, new_children, page_is_empty,
433
436
  overflow = (
434
437
  overflow or
435
438
  context.reported_footnotes or
436
- context.overflows_page(
437
- bottom_space, new_position_y + offset_y))
439
+ context.overflows_page(bottom_space, new_position_y + offset_y))
438
440
  if overflow:
439
441
  context.report_footnote(footnote)
440
442
  # If we've put other content on this page, then we may want
@@ -443,11 +445,15 @@ def _linebox_layout(context, box, index, child, new_children, page_is_empty,
443
445
  # even try.
444
446
  if new_children or not page_is_empty:
445
447
  if footnote.style['footnote_policy'] == 'line':
446
- next_lines = len(tuple(lines_iterator))
448
+ if needed := box.style['widows'] - 1:
449
+ for _ in lines_iterator:
450
+ needed -= 1
451
+ # Don’t iterate over all lines as it can be long.
452
+ if needed == 0:
453
+ break
447
454
  abort, stop, resume_at = _break_line(
448
- context, box, line, new_children,
449
- next_lines, page_is_empty, index,
450
- skip_stack, resume_at, absolute_boxes,
455
+ context, box, line, new_children, needed, page_is_empty,
456
+ index, skip_stack, resume_at, absolute_boxes,
451
457
  fixed_boxes)
452
458
  break_linebox = True
453
459
  break
weasyprint/layout/grid.py CHANGED
@@ -1123,6 +1123,7 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1123
1123
  # Find resume_at row.
1124
1124
  this_page_children = []
1125
1125
  resume_row = None
1126
+ extra_skip_height = 0
1126
1127
  if skip_stack:
1127
1128
  from .block import block_level_layout
1128
1129
  first_skip_row = min(skip_stack)
@@ -1130,7 +1131,6 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1130
1131
  skip_height = (
1131
1132
  sum(size for size, _ in rows_sizes[:last_skip_row]) +
1132
1133
  (len(rows_sizes[:last_skip_row]) - 1) * row_gap)
1133
- extra_skip_height = 0
1134
1134
  for child, (x, y, width, height) in children_positions.items():
1135
1135
  if (advancement := box.advancements.get((x, y))) is None:
1136
1136
  continue
@@ -1271,13 +1271,16 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1271
1271
  if child.margin_left == 'auto':
1272
1272
  child.margin_left = 0
1273
1273
 
1274
- child_width = width - (
1275
- child.margin_left + child.border_left_width + child.padding_left +
1276
- child.margin_right + child.border_right_width + child.padding_right)
1277
- child_height = height - (
1278
- child.margin_bottom + child.border_bottom_width + child.padding_bottom)
1274
+ child_border_width = width - (child.margin_left + child.margin_right)
1275
+ child_content_width = child_border_width - (
1276
+ child.border_left_width + child.padding_left +
1277
+ child.border_right_width + child.padding_right)
1278
+ child_border_height = height - child.margin_bottom
1279
+ child_content_height = child_border_height - (
1280
+ child.border_bottom_width + child.padding_bottom)
1279
1281
  if not child_skip_stack or child.style['box_decoration_break'] == 'clone':
1280
- child_height -= (
1282
+ child_border_height -= child.margin_top
1283
+ child_content_height -= (
1281
1284
  child.margin_top + child.border_top_width + child.padding_top)
1282
1285
 
1283
1286
  justify_self = set(child.style['justify_self'])
@@ -1286,6 +1289,9 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1286
1289
  if justify_self & {'normal', 'stretch'}:
1287
1290
  if child.style['width'] == 'auto':
1288
1291
  child.style = child.style.copy()
1292
+ child_width = (
1293
+ child_content_width if child.style['box_sizing'] == 'content-box'
1294
+ else child_border_width)
1289
1295
  child.style['width'] = Dimension(child_width, 'px')
1290
1296
  align_self = set(child.style['align_self'])
1291
1297
  if align_self & {'auto'}:
@@ -1293,6 +1299,9 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1293
1299
  if align_self & {'normal', 'stretch'}:
1294
1300
  if child.style['height'] == 'auto':
1295
1301
  child.style = child.style.copy()
1302
+ child_height = (
1303
+ child_content_height if child.style['box_sizing'] == 'content-box'
1304
+ else child_border_height)
1296
1305
  child.style['height'] = Dimension(child_height, 'px')
1297
1306
 
1298
1307
  # TODO: Find a better solution for the layout.
@@ -1338,11 +1347,11 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1338
1347
  continue
1339
1348
 
1340
1349
  if justify_self & {'normal', 'stretch'}:
1341
- new_child.width = max(child_width, new_child.width)
1350
+ new_child.width = max(child_content_width, new_child.width)
1342
1351
  else:
1343
1352
  if new_child.style['width'] == 'auto':
1344
1353
  new_child.width = max_content_width(context, new_child, outer=False)
1345
- diff = child_width - new_child.width
1354
+ diff = child_content_width - new_child.width
1346
1355
  if justify_self & {'center'}:
1347
1356
  new_child.translate(diff / 2, 0)
1348
1357
  elif justify_self & {'right', 'end', 'flex-end', 'self-end'}:
@@ -1350,9 +1359,9 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1350
1359
 
1351
1360
  # TODO: Apply auto margins.
1352
1361
  if align_self & {'normal', 'stretch'}:
1353
- new_child.height = max(child_height, new_child.height)
1362
+ new_child.height = max(child_content_height, new_child.height)
1354
1363
  else:
1355
- diff = child_height - new_child.height
1364
+ diff = child_content_height - new_child.height
1356
1365
  if align_self & {'center'}:
1357
1366
  new_child.translate(0, diff / 2)
1358
1367
  elif align_self & {'end', 'flex-end', 'self-end'}:
@@ -1368,8 +1377,9 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1368
1377
  context.finish_block_formatting_context(box)
1369
1378
  return None, None, {'break': 'any', 'page': None}, [], False
1370
1379
 
1371
- old_advancements = box.advancements or {}
1372
- advancements = box.advancements = {}
1380
+ old_advancements = box.advancements.copy()
1381
+ advancements = box.advancements
1382
+ advancements.clear()
1373
1383
  box = box.copy_with_children(new_children)
1374
1384
  if isinstance(box, boxes.InlineGridBox):
1375
1385
  # TODO: Synthetize a real baseline value.
@@ -1420,7 +1430,8 @@ def grid_layout(context, box, bottom_space, skip_stack, containing_block,
1420
1430
  else:
1421
1431
  # Child fully drawn, save the extra height added to reach the bottom of
1422
1432
  # the page to substract it from the advancements.
1423
- extra_advancement = max(extra_advancement, child.height - child_height)
1433
+ extra_advancement = max(
1434
+ extra_advancement, child.height - child_content_height)
1424
1435
 
1425
1436
  # Substract the extra height added to reach the bottom of the page from all the
1426
1437
  # advancements.
weasyprint/layout/page.py CHANGED
@@ -389,7 +389,7 @@ def make_margin_boxes(context, page, state):
389
389
  if box.is_generated:
390
390
  # @margins mustn't manipulate page-context counters
391
391
  margin_state = copy.deepcopy(state)
392
- quote_depth, counter_values, counter_scopes = margin_state
392
+ quote_depth, counter_values, counter_scopes, _page_groups = margin_state
393
393
  # TODO: check this, probably useless
394
394
  counter_scopes.append(set())
395
395
  build.update_counters(margin_state, box.style)
@@ -901,7 +901,7 @@ def _update_page_groups(page_groups, resume_at, next_page, root_box, blank):
901
901
  return next_page['page']
902
902
 
903
903
 
904
- def remake_page(index, page_groups, context, root_box, html):
904
+ def remake_page(index, context, root_box, html):
905
905
  """Return one laid out page without margin boxes.
906
906
 
907
907
  Start with the initial values from ``context.page_maker[index]``.
@@ -932,6 +932,7 @@ def remake_page(index, page_groups, context, root_box, html):
932
932
  (next_page_side == 'right' and not right_page) or
933
933
  (context.reported_footnotes and resume_at is None))
934
934
  side = 'right' if right_page else 'left'
935
+ page_groups = page_state[3]
935
936
  name = _update_page_groups(page_groups, resume_at, next_page, root_box, blank)
936
937
  groups = tuple((name, index) for name, index, _ in page_groups)
937
938
  page_type = PageType(side, blank, name, index, groups)
@@ -990,7 +991,6 @@ def make_all_pages(context, root_box, html, pages):
990
991
  """
991
992
  i = 0
992
993
  reported_footnotes = None
993
- page_groups = []
994
994
  while True:
995
995
  remake_state = context.page_maker[i][-1]
996
996
  if (len(pages) == 0 or
@@ -1002,7 +1002,7 @@ def make_all_pages(context, root_box, html, pages):
1002
1002
  remake_state['pages_wanted'] = False
1003
1003
  remake_state['anchors'] = []
1004
1004
  remake_state['content_lookups'] = []
1005
- page, resume_at = remake_page(i, page_groups, context, root_box, html)
1005
+ page, resume_at = remake_page(i, context, root_box, html)
1006
1006
  reported_footnotes = context.reported_footnotes
1007
1007
  yield page
1008
1008
  else:
@@ -101,9 +101,32 @@ def _block_content_width(context, box, function, outer):
101
101
  function(context, child, outer=True) for child in box.children
102
102
  if not child.is_absolutely_positioned()]
103
103
  width = max(children_widths) if children_widths else 0
104
+ elif box.style['box_sizing'] == 'content-box':
105
+ width = width.value
104
106
  else:
105
- assert width.unit.lower() == 'px'
106
107
  width = width.value
108
+ percentages = 0
109
+
110
+ for value in ('padding_left', 'padding_right'):
111
+ style_value = box.style[value]
112
+ if style_value != 'auto' and not check_math(style_value):
113
+ if style_value.unit.lower() == 'px':
114
+ width -= style_value.value
115
+ else:
116
+ assert style_value.unit == '%'
117
+ percentages += style_value.value
118
+
119
+ # Same as margin_width().
120
+ collapse = box.style['border_collapse'] == 'collapse'
121
+ if collapse and hasattr(box, 'border_left_width'):
122
+ width -= box.border_left_width
123
+ else:
124
+ width -= box.style['border_left_width']
125
+ if collapse and hasattr(box, 'border_right_width'):
126
+ width -= box.border_right_width
127
+ else:
128
+ width -= box.style['border_right_width']
129
+ width = (100 - min(100, percentages)) * max(0, width) / 100
107
130
 
108
131
  return adjust(box, outer, width)
109
132
 
@@ -152,7 +175,7 @@ def margin_width(box, width, left=True, right=True):
152
175
  (['margin_right', 'padding_right'] if right else [])
153
176
  ):
154
177
  style_value = box.style[value]
155
- if style_value != 'auto':
178
+ if style_value != 'auto' and not check_math(style_value):
156
179
  if style_value.unit.lower() == 'px':
157
180
  width += style_value.value
158
181
  else:
@@ -240,7 +263,7 @@ def inline_max_content_width(context, box, outer=True, is_line_start=False):
240
263
  def column_group_content_width(context, box):
241
264
  """Return the *-content width for a ``TableColumnGroupBox``."""
242
265
  width = box.style['width']
243
- if width == 'auto' or width.unit == '%':
266
+ if width == 'auto' or check_math(width) or width.unit == '%':
244
267
  width = 0
245
268
  else:
246
269
  assert width.unit.lower() == 'px'
@@ -362,7 +385,17 @@ def inline_line_widths(context, box, outer, is_line_start, minimum, skip_stack=N
362
385
  # "By default, there is a break opportunity
363
386
  # both before and after any inline object."
364
387
  if minimum:
365
- lines = [None, min_content_width(context, child), None]
388
+ # "For soft wrap opportunities defined by the boundary between two
389
+ # characters or atomic inlines, the white-space property on the nearest
390
+ # common ancestor of the two characters controls breaking; which
391
+ # elements’ line-break, word-break, and overflow-wrap properties control
392
+ # the determination of soft wrap opportunities at such boundaries is
393
+ # undefined in this level." We choose to always follow the parent’s
394
+ # value here, other parts of the line-breaking algorithm do the same.
395
+ if box.style['white_space'] in ('normal', 'pre-wrap', 'pre-line'):
396
+ lines = [None, min_content_width(context, child), None]
397
+ else:
398
+ lines = [min_content_width(context, child)]
366
399
  else:
367
400
  lines = [max_content_width(context, child)]
368
401
  # The first text line goes on the current line.
@@ -564,21 +597,22 @@ def table_and_columns_preferred_widths(context, box, outer=True):
564
597
  # Define constrainedness
565
598
  constrainedness = [False for i in range(grid_width)]
566
599
  for i in range(grid_width):
567
- if (column_groups[i] and column_groups[i].style['width'] != 'auto' and
568
- column_groups[i].style['width'].unit != '%'):
569
- constrainedness[i] = True
570
- continue
571
- if (columns[i] and columns[i].style['width'] != 'auto' and
572
- columns[i].style['width'].unit != '%'):
573
- constrainedness[i] = True
574
- continue
575
- for cell in zipped_grid[i]:
576
- if (cell and cell.colspan == 1 and
577
- cell.style['width'] != 'auto' and
578
- not check_math(cell.style['width']) and
579
- cell.style['width'].unit != '%'):
600
+ if column_groups[i]:
601
+ width = column_groups[i].style['width']
602
+ if width != 'auto' and not check_math(width) and width.unit != '%':
580
603
  constrainedness[i] = True
581
- break
604
+ continue
605
+ if columns[i]:
606
+ width = columns[i].style['width']
607
+ if width != 'auto' and not check_math(width) and width.unit != '%':
608
+ constrainedness[i] = True
609
+ continue
610
+ for cell in zipped_grid[i]:
611
+ if cell and cell.colspan == 1:
612
+ width = cell.style['width']
613
+ if width != 'auto' and not check_math(width) and width.unit != '%':
614
+ constrainedness[i] = True
615
+ break
582
616
 
583
617
  intrinsic_percentages = [
584
618
  min(percentage, 100 - sum(intrinsic_percentages[:i]))
@@ -646,7 +680,8 @@ def table_and_columns_preferred_widths(context, box, outer=True):
646
680
  sum(max_content_widths), large_percentage_contribution,
647
681
  *small_percentage_contributions]))
648
682
 
649
- if table.style['width'] != 'auto' and table.style['width'].unit.lower() == 'px':
683
+ width = table.style['width']
684
+ if width != 'auto' and not check_math(width) and width.unit.lower() == 'px':
650
685
  # "percentages on the following properties are treated instead as
651
686
  # though they were the following: width: auto"
652
687
  # https://dbaron.org/css/intrinsic/#outer-intrinsic
@@ -681,12 +716,16 @@ def replaced_min_content_width(box, outer=True):
681
716
  width = box.style['width']
682
717
  if width == 'auto':
683
718
  height = box.style['height']
684
- if height == 'auto' or height.unit == '%':
719
+ if height == 'auto' or check_math(height) or height.unit == '%':
685
720
  height = 'auto'
686
721
  else:
687
722
  assert height.unit.lower() == 'px'
688
723
  height = height.value
689
- if box.style['max_width'] != 'auto' and box.style['max_width'].unit == '%':
724
+ unknown_max_width = (
725
+ box.style['max_width'] != 'auto' and
726
+ not check_math(box.style['max_width']) and
727
+ box.style['max_width'].unit == '%')
728
+ if unknown_max_width:
690
729
  # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
691
730
  width = 0
692
731
  else:
@@ -697,7 +736,7 @@ def replaced_min_content_width(box, outer=True):
697
736
  width, _ = default_image_sizing(
698
737
  intrinsic_width, intrinsic_height, intrinsic_ratio, 'auto',
699
738
  height, default_width=0, default_height=0)
700
- elif box.style['width'].unit == '%':
739
+ elif check_math(box.style['width']) or box.style['width'].unit == '%':
701
740
  # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
702
741
  width = 0
703
742
  else:
@@ -711,7 +750,7 @@ def replaced_max_content_width(box, outer=True):
711
750
  width = box.style['width']
712
751
  if width == 'auto':
713
752
  height = box.style['height']
714
- if height == 'auto' or height.unit == '%':
753
+ if height == 'auto' or check_math(height) or height.unit == '%':
715
754
  height = 'auto'
716
755
  else:
717
756
  assert height.unit.lower() == 'px'
@@ -723,7 +762,7 @@ def replaced_max_content_width(box, outer=True):
723
762
  width, _ = default_image_sizing(
724
763
  intrinsic_width, intrinsic_height, intrinsic_ratio, 'auto', height,
725
764
  default_width=300, default_height=150)
726
- elif box.style['width'].unit == '%':
765
+ elif check_math(box.style['width']) or box.style['width'].unit == '%':
727
766
  # See https://drafts.csswg.org/css-sizing/#intrinsic-contribution
728
767
  width = 0
729
768
  else:
@@ -9,6 +9,7 @@ from .. import VERSION, Attachment
9
9
  from ..html import W3C_DATE_RE
10
10
  from ..logger import LOGGER, PROGRESS_LOGGER
11
11
  from ..matrix import Matrix
12
+ from ..urls import select_source
12
13
  from . import debug, pdfa, pdfua, pdfx
13
14
  from .fonts import build_fonts_dictionary
14
15
  from .stream import Stream
@@ -273,14 +274,24 @@ def generate_pdf(document, target, zoom, **options):
273
274
  key = key.encode('ascii', errors='ignore').decode()
274
275
  if key:
275
276
  pdf.info[key] = pydyf.String(value)
277
+ if options['xmp_metadata']:
278
+ for url in options['xmp_metadata']:
279
+ result = select_source(url)
280
+ with result as (file_obj, base_url, charset, _):
281
+ xmp_metadata = file_obj.read()
282
+ if charset:
283
+ xmp_metadata = xmp_metadata.decode(charset).encode()
284
+ metadata.xmp_metadata.append(xmp_metadata)
276
285
 
277
286
  # Embedded files
278
287
  attachments = metadata.attachments.copy()
279
288
  if options['attachments']:
289
+ relationships = iter(options['attachment_relationships'] or [])
280
290
  for attachment in options['attachments']:
281
291
  if not isinstance(attachment, Attachment):
282
292
  attachment = Attachment(
283
- attachment, url_fetcher=document.url_fetcher)
293
+ attachment, url_fetcher=document.url_fetcher,
294
+ relationship=next(relationships, 'Unspecified'))
284
295
  attachments.append(attachment)
285
296
  pdf_attachments = []
286
297
  for attachment in attachments:
weasyprint/pdf/anchors.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """Insert anchors, links, bookmarks and inputs in PDFs."""
2
2
 
3
3
  import collections
4
- import io
5
4
  import mimetypes
6
5
  from hashlib import md5
7
6
  from os.path import basename
@@ -330,18 +329,12 @@ def write_pdf_attachment(pdf, attachment, compress):
330
329
  """Write an attachment to the PDF stream."""
331
330
  # Attachments from document links like <link> or <a> can only be URLs.
332
331
  # They're passed in as tuples
333
- url = None
334
- uncompressed_length = 0
335
- stream = b''
332
+ url = mime_type = None
336
333
  try:
337
- with attachment.source as (_, source, url, _):
338
- if isinstance(source, str):
339
- source = source.encode()
340
- if isinstance(source, bytes):
341
- source = io.BytesIO(source)
342
- for data in iter(lambda: source.read(4096), b''):
343
- uncompressed_length += len(data)
344
- stream += data
334
+ with attachment.source as (file_obj, url, _, mime_type):
335
+ stream = file_obj.read()
336
+ if isinstance(stream, str):
337
+ stream = stream.encode()
345
338
  except URLFetchingError as exception:
346
339
  LOGGER.error('Failed to load attachment: %s', exception)
347
340
  LOGGER.debug('Error while loading attachment:', exc_info=exception)
@@ -356,9 +349,10 @@ def write_pdf_attachment(pdf, attachment, compress):
356
349
  filename = basename(unquote(urlsplit(url).path))
357
350
  else:
358
351
  filename = 'attachment.bin'
359
- mime_type = mimetypes.guess_type(filename, strict=False)[0]
360
- if not mime_type:
361
- mime_type = 'application/octet-stream'
352
+ mime_type = (
353
+ mime_type or
354
+ mimetypes.guess_type(filename, strict=False)[0] or
355
+ 'application/octet-stream')
362
356
 
363
357
  creation = pydyf.String(attachment.created.strftime('D:%Y%m%d%H%M%SZ'))
364
358
  mod = pydyf.String(attachment.modified.strftime('D:%Y%m%d%H%M%SZ'))
@@ -367,7 +361,7 @@ def write_pdf_attachment(pdf, attachment, compress):
367
361
  'Subtype': f'/{mime_type.replace("/", "#2f")}',
368
362
  'Params': pydyf.Dictionary({
369
363
  'CheckSum': f'<{attachment.md5}>',
370
- 'Size': uncompressed_length,
364
+ 'Size': len(stream),
371
365
  'CreationDate': creation,
372
366
  'ModDate': mod,
373
367
  })
weasyprint/pdf/fonts.py CHANGED
@@ -618,14 +618,23 @@ def _build_vector_font_dictionary(font_dictionary, pdf, font, widths, compress,
618
618
  if font.missing:
619
619
  # Add CMap that doesn’t include missing glyphs, so that they can be replaced by
620
620
  # .notdef.
621
+ cmap_extra = pydyf.Dictionary({
622
+ 'Type': '/CMap',
623
+ 'CMapName': '/WP-Encod-0',
624
+ 'CIDSystemInfo': pydyf.Dictionary({
625
+ 'Registry': pydyf.String('Adobe'),
626
+ 'Ordering': pydyf.String('Identity'),
627
+ 'Supplement': 0,
628
+ }),
629
+ })
621
630
  encoding = pydyf.Stream([
622
631
  b'/CIDInit /ProcSet findresource begin',
623
632
  b'12 dict begin',
624
633
  b'begincmap',
625
634
  b'/CIDSystemInfo',
626
635
  b'3 dict dup begin',
627
- b'/Registry (WP) def',
628
- b'/Ordering (Encod) def',
636
+ b'/Registry (Adobe) def',
637
+ b'/Ordering (Identity) def',
629
638
  b'/Supplement 0 def',
630
639
  b'end def',
631
640
  b'/CMapName /WP-Encod-0 def',
@@ -633,7 +642,7 @@ def _build_vector_font_dictionary(font_dictionary, pdf, font, widths, compress,
633
642
  b'1 begincodespacerange',
634
643
  b'<0000> <ffff>',
635
644
  b'endcodespacerange',
636
- ], compress=compress)
645
+ ], cmap_extra, compress=compress)
637
646
  available = tuple(font.to_unicode)
638
647
  available_length = len(available)
639
648
  for i in range(ceil(available_length / 100)):