Commit Graph
6238 Commits
Author SHA1 Message Date
Grzegorz MartensandClaude Opus 5.5 042371bda9 M8: a test of its own for each operand read the fix changes
Review of the whole change found reads it fixes that no test failed
without: each was put back to main's read alone, and the tests passed.
Each now has a case that fails without it:
- the proof's `Tf` size (text starting outside the region reaches into
  it only at its size) and font (an undecodable font's text leaves the
  region unverifiable), and the strings a `TJ` carries;
- the proof's `l`, `c`, `v` and `y` points after a surplus, from a point
  outside the region, and the walk's `m`, `l`, `c`, `v` and `y` points
  beside a mark, each alone;
- the property list a second page sharing the resources still names;
- a Type 3 font an ExtGState's `/Font` sets, kept by the page and its
  glyph by the font, and one without resources of its own, which keeps
  the page's pattern its glyph may paint with;
- a Type 3 font only a soft mask's group selects, the mask's ExtGState
  named after a surplus;
- the named property lists read once, with each `BDC` naming another
  entry, so that it is the lists, not each name's answer, that are kept.

And the walk's `cs` says why it tells a Pattern space by the name
`/Pattern` alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-10 23:11:09 +01:00
Grzegorz MartensandClaude Opus 5.5 5e2552dcb0 M8: named property lists and their MCIDs are read once for a stream
The tags cleanup resolved the page's `/Properties` at every named `BDC`,
on main too: 20,000 of them with 5,000 entries took 28 s, as measured in
review (optimized), against 0.07 s with one entry. And the second commit's
`MCID` by reference was read again at every `BDC`: the document keeps an
object larger than its cache only until it reads another, so reading the
`MCID` evicted a property list that large, which was parsed again, all of
it, at the next `BDC` (50 `BDC` took 12.3 s against 0.44 s with the
`MCID` given directly). Now the named property lists are read once for
the stream, in place when they are direct, and each name's `MCID` once.

Tests (`crates/redact/tests/operator_work.rs`, a binary of its own whose
tests run one at a time), each the least of three runs against one entry
or operator, allowing ten times as long plus a second:
`named_property_lists_are_read_once_for_a_page` (5,000 `BDC`, 2,000
entries) and `an_oversized_property_list_is_read_once_for_a_stream` (100
`BDC` naming a list too large to keep).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-10 23:11:09 +01:00
Grzegorz MartensandClaude Opus 5.5 7c78222612 M8: path points and an MCID by reference are read as the renderer does
Review of the operand reads. Path construction was still read from the
front, in the walk (`construct_path`) and in the proof (`extend_path`):
after a surplus number, `9 100 100 m 9 140 100 l ...` placed the square at
x = 9, so a mark over it removed nothing, and an even surplus placed a
square beside a mark under it, which was removed. `m` and `l` now take
their last two numbers, `c` six, `v` and `y` four, and the proof's `re`
four, as the walk's already did.

The tags cleanup read an `MCID` with `Dict::int`, so one given by
reference in a named property list, or in a structure element's `/MCR`,
was missed and the element kept the removed text in `/ActualText` and
`/Alt`. It is read through the reference now.

Tests: `a_paths_points_after_a_surplus_number_are_where_the_renderer_draws_them`
(lines, and curves by `c`, `v` and `y`),
`an_mcid_given_by_reference_loses_what_redaction_removed` (and an `/MCR` of a
form's stream that stays), and
`the_proof_reads_a_paths_points_from_the_end` (lines, `re`, and each of
`c`, `v` and `y` from a point outside the region).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-10 22:22:09 +01:00
Grzegorz MartensandClaude Opus 5.5 b16f0c0a77 M8: redaction reads an operator's operands from the end, as the renderer does
An operator takes its operands from the end, and the renderer ignores a
surplus before them: `(x) /OC /MC0 BDC` marks a layer's block, and
`(x) /GS1 gs` selects `/GS1`. Redaction read some of them from the front,
so a surplus stood in for them and what they named went unseen. Found in
review of #6882, where Remove Hidden Information left the text of a
hidden layer in `(x) /OC /MC0 BDC (LAYERSECRET) Tj EMC`, which both the
app's renderer and Poppler hide.

In what Apply Redactions and Remove Hidden Information decide:
- the layers walk read a block's tag and property list from the front,
  and kept a hidden layer's block;
- the cleanup of `/ActualText`, `/Alt`, `/E` and `/T` in a block whose
  paint was removed read (and wrote) the property list from the front,
  and kept the name that still read the removed word;
- the tags cleanup read a block's `MCID` from the front, and left the
  removed text in its structure element's `/ActualText` and `/Alt`; and a
  `TJ`'s array, so a block that still drew was taken for empty and its
  structure element lost the content the page still shows;
- the property cleanups read the name of a `BDC` or `DP` property list
  from the front, and retired a property list the page still draws with;
- the walk read the ExtGState a `gs` selects from the front, so a font set
  only that way was not set, and `cs`/`CS` the same for a pattern space;
- the Type 3 cleanup's reads of `cs`, `gs`, `Tf`, `BDC` and `DP`, which
  decide which resources and fonts are still used.
The redaction proof read `Tc`, `Tw`, `Tz`, `TL`, `Ts`, `Tf`, `"`, `TJ` and
`Do` from the front too. `pdfcraft_content::Op` gains `back`, `name_back`
and `num_back`: operand `k` counted from the end.

Tests (`crates/redact/src/tests/operands_from_the_end.rs`): a hidden
layer's block, `/ActualText` on removed paint, a tag's `MCID`, a tagged
`TJ` that still draws, a property list still drawn with, a font from an
ExtGState, the pattern a `/Pattern cs` keeps, and a Type 3 font with the
ExtGState, colour space, property list and font its glyph uses, each
after a surplus operand. In `tests_verify.rs`,
`the_proof_reads_operands_from_the_end` (`Tf`, `TJ`, and `Do` of a form
and of images), `the_proof_reads_the_text_states_operands_from_the_end`
(`Tc`, `Tw`, `Tz`, `TL`, `Ts` and `"`) and
`an_image_a_type3_glyph_draws_after_a_surplus_operand_is_found`. And
`operands_are_counted_from_the_end` in the content crate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
2026-11-10 22:22:09 +01:00
Kyle KassenandFredApps 919db9bd51 M10: preserve NoRotate appearance geometry in vector printing (#5036)
* M10: preserve annotation placement precision (#4995)

* M10: preserve NoRotate and implicit Text print orientation (#5029)

Keep existing printable appearances upright on rotated source pages, including Text annotations whose NoRotate flag is clear.
Preserve original flags and appearance streams, print eligibility and outer page transforms.

* M10: clarify NoRotate verification docs

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 17:04:04 +01:00
6cded6a5a3 M10: a booklet's both sides follow a driver that keeps its own two-sided setting (#5772) (#5903)
* M10: a booklet's both sides follow a driver that keeps its own two-sided setting (#5772)

Booklet ▸ Both sides always asked for two-sided printing on the short
edge (#2707). A driver that doesn't take two-sided settings from other
programs (no two-sided printing, manual two-sided only in its
Properties, Microsoft Print to PDF) then made craft-print refuse the
whole job. The reporter's Epson ET-2870 offers only OneSided, and its
manual two-sided setting is what prints their normal two-sided jobs.

print::booklet_job_duplex asks such a driver for nothing on both
sides, so its own setting applies, as for ordinary printing (#2422)
and as Acrobat enables two-sided for a booklet only "if available".
Unknown support still asks for the short edge; one side on its own
stays one-sided. The Print dialog takes the chosen printer's support
from the printer list (PrintDraft::printer_two_sided, also behind its
"its own setting applies" note), and doc_print reads it only for a
booklet's both sides.

The dialog's booklet test used the machine's printers, and on Windows
the default, Microsoft Print to PDF, now gives the other answer: it
sets its own printers and checks the note too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M10: doc_print's lookup of a booklet printer's two-sided support is tested (#5772)

The booklet test fed job_duplex the printer's answer directly, so a
lookup that missed the default printer or matched the wrong one would
have gone unnoticed. The search over the printer list is now its own
function, two_sided_in, tested with a list of named, default and
unknown printers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M10: a booklet printed to "default" goes to the printer whose driver was asked (#5772)

doc_print decides a booklet's both sides from the printer's two-sided
support. For "default", the job named no printer, so the spooler picked
whichever printer was the default when the job was submitted, after the
document was rendered. The printer found in the list is now the job's
printer, by name, and the lookup test covers a one-sided default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* #5903: the booklet test lists its printers through a stand-in source

It set the dialog's printer list by hand while the real list was still loading. On a machine with
no printers (CI's Linux runner) the list that arrived replaced them and reset the printer, so the
dialog the test clicked through was not the one it set up. The printers now come from the
dialog's own source, drivers unasked as #3554 lists them, with each driver's two-sided answer in
the map.

---------

Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 16:34:48 +01:00
0e4b9171c0 fix(ui-egui): keep hotbar picker stationary while resizing (#6513)
* fix(ui-egui): keep hotbar picker stationary while resizing

* fix(ui-egui): keep hotbar picker pinned during keyboard resize

Pin the Hotbar tools picker while the Toolbar size slider is adjusted with the arrow keys, until the slider loses focus. Widen the regression test to both drag directions and every layout, add a keyboard test, and document the new helpers.

* fix(ui-egui): closing the hotbar picker ends a size adjustment

close_picker() also clears picker_drag_pos and keyboard_sizing. Main's
close_hidden_picker() closes the picker on passes that don't draw the
hotbar, where show() can't clear them, and the next opening would start
at the held position. Adds closing_the_picker_ends_a_size_adjustment, a
doc comment for picker_drag_pos, and says "picker" where the comment
meant it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 16:24:33 +01:00
3bcaacc272 fix(print): keep the localized reset label off the properties panel title (#5550)
* fix(print): keep the localized reset label off the properties panel title

The header of Print ▸ Properties… laid the title out first and gave what
was left of the row to Done and Reset. When the translated "Reset to
printer defaults" needs more room than that, egui keeps placing the two
buttons from the right edge of the child leftwards and paints them back
over the title: in Italian the label is 51 characters against 25 in
English, and both strings end up unreadable (#5545).

Laid out with egui::Sides::shrink_left().truncate instead: the buttons
take their room first and the title takes what is left of the row, cut
with an ellipsis. Nothing moves in English, where the title still fits.

* test(print): pin the header against every catalog and the title's tooltip

The geometry tests behind #5545 tried English, Italian and German at four
widths. They now walk every catalogued language at the row the settings column
really gives the header (470 px less the frame's 10 px margins), so the longest
translation of "Reset to printer defaults" is covered by the run instead of by a
guess at which locale is the worst one.

Two more tests: hovering the elided title opens a tooltip with the full string,
while a title that fits whole opens none; and a row too narrow to give the title
even an ellipsis leaves it out instead of painting it back over the buttons.

That last case is why driver_options_header returns Option<Response>. Bulgarian
(370 px of button), Spanish (365) and Ukrainian (361) fill the row with the two
buttons alone at 450 px: the ellipsis is 12.4 px wide and the clearance was
0.1 px, so a pixel less and the label was painted over the buttons again.

---------

Co-authored-by: Jarvis (Hermes) <jarvis@localhost>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 16:19:34 +01:00
FredAppsandKyle Kassen afca38ad71 M10: retain printed artwork with indirect annotation metadata (lands #4668) (#6855)
* M10: retain printed artwork with indirect annotation rectangles

* M10: resolve indirect annotation appearance states when printing

* M10: follow bounded annotation reference chains when printing

---------

Co-authored-by: Kyle Kassen <85040622+KyleKassen@users.noreply.github.com>
2026-11-10 16:05:43 +01:00
905e9abf6f fix(forms): ignore print requests from automatic field scripts (#4166)
* fix(forms): ignore print requests from automatic field scripts

* docs(contributors): opt in to GitHub profile name sync

---------

Co-authored-by: enriquemelzer-bit <319226743+enriquemelzer-bit@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 15:50:16 +01:00
Kyle KassenandFredApps debb826f29 M10: preserve annotation placement precision (#4995) (#5002)
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 15:17:15 +01:00
BFlat 1a159c57fc Merge pull request #6820 from storytold/merge/print-snapshot-area-2491
Print a selected area from Take a Snapshot (lands #2491)
2026-11-10 19:58:04 +07:00
BFlat 4ddf52ef90 Merge pull request #6824 from storytold/merge/egui-725a39fc-6699
Bump storytold/egui to 725a39fc (lands #6699)
2026-11-10 19:23:37 +07:00
huppifluppandFredApps 8864903795 M10: Recognize text no longer clips the first and last letters of a line (#5058)
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 19:23:21 +07:00
KiwiKwiandBabaKush 4a83ec87f5 fix(annot): drop Apple Markup's stale copy when a page's annotations change (#2829)
iOS Markup keeps a private copy of a page's markup (/AAPL:PPK, /AAPL:PPKHash on the page,
/AAPL:AKExtras on each annotation) and draws from it. Once /Annots changes, that copy is stale:
iOS 26 drew a Markup signature twice. Adding, deleting or answering a comment now drops the copy,
records the drop in the repair log, and leaves a refused edit untouched.

The change lives in lookup.rs (change_annots), called from create.rs (add) and modify.rs (delete,
reply, mark). Each of the four operations has a regression test on an Apple Markup page.

Co-authored-by: BabaKush <101988920+BabaKush@users.noreply.github.com>
2026-11-10 12:38:55 +01:00
FredApps 956935bf0b Merge main into #6820: the area's sheet uses the chosen paper's size, custom papers included (#6830) 2026-11-10 12:36:45 +01:00
e243022792 Print: custom paper sizes in the Print dialog, kept per printer with its setup (re-lands #2381) (#6830)
Brings back #2381 (by regendorf-ux), which #6706 reverted before v0.20.0 because its page-setup
rows made the Print dialog as tall as Preferences and failed the dialog-height test.

- Custom… beside Paper adds a named size in the preferences' unit (1 to 200 inches a side),
  kept with the settings; Remove takes it off the list. doc_print takes sizes such as 8.5x14in.
- #2381's "Remember this paper for <printer>" box and its separate printer-to-paper map are left
  out: since #6779 every printer keeps its paper, sizing and two-sided choice on its own (#2001).
  That setup now keeps the paper by name, so a custom paper is found again however the list
  changes, and one removed since leaves the paper as it is.
- The dialog scrolls within the screen since #6223, so the Custom… row no longer grows it.
- The Print part of the settings restore is its own function (clippy's complexity limit).

Co-authored-by: regendorf-ux <osvaldo.duran09@gmail.com>
Co-authored-by: regendorf-ux <338149330+regendorf-ux@users.noreply.github.com>
2026-11-10 11:43:49 +01:00
4bff30cf62 M10: Windows prints a large image whole: drawn as tiles under Windows.Data.Pdf's cap (#5131) (#5897)
* M10: Windows prints a large image whole: drawn as tiles under Windows.Data.Pdf's cap (#5131)

Windows prints each page through Windows.Data.Pdf, which draws any image
of more than 2^22 pixels from a copy shrunk to that size. With the page
at the printer's resolution (craft-libs#396, #5023), a 6000 x 6000 scan
still printed as 2048 x 2048, its one-pixel detail gone.

pdfcraft_optimize::tile_large_images replaces each such image XObject
with a form XObject of the same object number that draws it as tiles of
at most 2000 pixels a side, so whatever drew the image draws the tiles.
Samples are copied as stored (any bit depth and colour space, decode
array, interpolation, colour key); a grey or RGB JPEG is decoded and
stored with Flate; a soft or stencil mask of the image's size is cut
into the same tiles. Tiles overlap by a pixel against drivers that
soften image edges, except under a soft mask, where the overlap would
blend see-through pixels twice. JPEG 2000, CMYK or ambiguous JPEGs,
masks of another size, more than 80 million pixels, short data and
images past the decode allowance are left whole.

pdfcraft_engine::spooler::spooler_copy applies it to the copy that goes
to the spooler on Windows, from the Print dialog's worker and doc_print;
elsewhere, or when the copy can't be read or written, the bytes go as
they are. The saved print-ready PDF is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M10: a test that the spooler gets the tiles on Windows; tiles are up to 2001 px (#5131)

spooler_copy was only tested with prints that pass through unchanged, so
one that skipped tiling on Windows would have passed. A test now checks
that on Windows it sends the tiled copy and elsewhere the bytes as they
are. The optimize README said tiles have at most 2000 pixels a side;
with the overlap they have 2001.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* M10: the tiles' samples are StoredSamples, apart from the crate's Samples (#5131)

main has since added a public `Samples` (8-bit grey or RGB, for tools
that repaint an image), so two different things in one crate shared a
name. The tiles keep an image's samples as stored, in its own colour
space and depth, so each tile redraws exactly what it replaces; the
type now says so in its name and doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: austin62jake <340374338+austin62jake@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-10 10:25:54 +01:00
bflatastic e987f46fb6 Snapshot tab tests share one harness on the shared page fixture 2026-11-10 15:26:39 +07:00
bflatastic 3b23494513 Merge remote-tracking branch 'origin/main' into merge/egui-725a39fc-6699 2026-11-10 15:24:11 +07:00
regendorf-ux b7cfbd3980 Find and replace text in Edit text mode: a Replace row in the Find box, Replace all as one undo step (#2467) 2026-11-10 15:19:55 +07:00
bflatastic 51a87eb3d9 Give the snapshot-area booklet test main's creep field 2026-11-10 14:27:17 +07:00
bflatastic 1726d9aca3 Merge remote-tracking branch 'origin/main' into merge/print-snapshot-area-2491 2026-11-10 14:27:02 +07:00
BFlat 2b46f8bf8c Merge pull request #5698 from Piohrun/fix/jbig2-dictionary-any-route
fix(redact): a shared JBIG2 dictionary leaves with whatever used it, by any route
2026-11-10 14:24:09 +07:00
Hazel Granados 45d51d8c46 M3: a file named with open parameters opens at their destination (#3425) (#5049) 2026-11-10 14:23:54 +07:00
ljkx dec9f3600b Windows: Tesseract and the --control tools start without a console window (#6735) 2026-11-10 14:23:38 +07:00
BFlat 06572d57df Merge pull request #6805 from storytold/merge/redact-forms-6341
M8: redaction keeps an image a form without resources still draws (#6341, merged with main)
2026-11-10 14:22:51 +07:00
BFlat f48cf6eeba Merge pull request #6806 from storytold/merge/mixed-script-2778
fix(edit): preserve source fonts in mixed-script paragraphs (#2778, merged with main)
2026-11-10 14:22:36 +07:00
Aleksander Helgaker a1769246bd fix(ui): keys under a dialog or a menu leave the pages alone (keyboard routing, part 1) (#6608) 2026-11-10 14:22:20 +07:00
Eric Kaden ea2e13370c M12: dynamic XFA forms tab in their template's traversal order (#4149) 2026-11-10 14:22:02 +07:00
mtlcl410 67cd69050e M7: paragraph edits keep the advance of the text they drop (edit.text-edit) (#2968) 2026-11-10 14:21:29 +07:00
bflatastic c6740a16d8 Merge remote-tracking branch 'origin/main' into merge/redact-forms-6341
# Conflicts:
#	crates/edit/src/tests.rs
2026-11-10 10:46:07 +07:00
bflatastic 10755a9da9 Merge remote-tracking branch 'origin/main' into merge/mixed-script-2778
# Conflicts:
#	parity/acrobat-features.toml
2026-11-10 10:45:45 +07:00
BFlat 274b7a0424 Merge pull request #6800 from storytold/merge/fonts-budget-6239
M7: a font's metrics can be read within a caller's decode allowance (#6239, merged with main)
2026-11-10 10:43:29 +07:00
Piohrun e2464e1440 M2: a JBIG2 mask with a large symbol dictionary draws, and one a decoder limit refuses takes its picture with it (#6722) (#6784) 2026-11-10 10:43:13 +07:00
mtlcl410 d9ec80243b M12: XFA forms with many repeated rows lay out in linear time (form.xfa-dynamic) (#6776) 2026-11-10 10:42:57 +07:00
BFlat 85e2ebfac8 Updates test: answer the startup prompt before clicking in Preferences (#6799) 2026-11-10 10:42:41 +07:00
Osvaldo Duran MelendezandClaude Sonnet 5.5 6034de11bf Print a selected area: the automation test reads the printed PDF (sheet size, the area's clip rectangle, the page drawn through it)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 22:22:43 -05:00
bflatastic f6fd408419 Merge remote-tracking branch 'origin/main' into merge/fonts-budget-6239
# Conflicts:
#	crates/edit/src/tests.rs
2026-11-10 10:09:08 +07:00
jonathanwxh-cell ccda1b0a1e fix(export): skip empty HTML runs and clamp heading levels to h6 (#6136) 2026-11-10 10:02:47 +07:00
Hitesh ba8beb12b9 fix(ui): All tools ▸ Edit a PDF turns on Edit text & images at once (#4605) 2026-11-10 10:02:30 +07:00
Eric Kaden 5af40c5515 M6: form buttons open the files attached to their document (exportDataObject) (#3575) 2026-11-10 10:02:13 +07:00
robertobulltech 851d6ceca5 M10 #5022: preserve printable Link appearances (#5028) 2026-11-10 10:01:52 +07:00
jonathanwxh-cell 466e1971d6 fix(annot,ui-egui): guard comment edits against non-comments and stale state (#3916) (#4673) 2026-11-10 10:01:09 +07:00
ljkx 0d853dace7 M6: the Calculate tab shows and edits a field's custom calculation script (#6630) (#6717) 2026-11-10 10:00:52 +07:00
Ken Mantle 5b67f8d021 Booklet printing: creep compensation (#6790) 2026-11-10 10:00:36 +07:00
jonathanwxh-cell c6d9c13002 fix(ui): keep added image corner resize responsive and clip out-of-bounds drag preview (#4477, #3938) (#4625) 2026-11-10 10:00:18 +07:00
jonathanwxh-cell 8e6db09459 Zeroize key material, KDF buffers, and UI password drafts (#3928) (#4427) 2026-11-10 10:00:01 +07:00
.CooCoo 709f5fb80f Updates: Restart to Update with unsaved changes opens the save prompt (#6726) 2026-11-10 09:59:45 +07:00
jonathanwxh-cell 9ccd6964ee fix(preflight): handle typeless annotations, indexed colour, and indirect filters (#5279) 2026-11-10 09:59:28 +07:00