Commit Graph
1880 Commits
Author SHA1 Message Date
dotCooCoo 0ccc6d9df5 .ai import: paths keep their notes and Show Center
Squashed from #5129 onto main.
2026-11-02 12:03:35 +07:00
dotCooCoo db11446502 .ai import: legacy type keeps its spacing, hanging punctuation and hyphenation
Legacy type (`To` ... `TO`) now reads `min opt max TC` and `min opt max TW`
into the paragraph's letter and word spacing, `hangingQuotes Tq` into Roman
hanging punctuation, and `hyphenate limit lead tail max Th` into hyphenation
on or off, After First, Before Last and the Hyphen Limit (0 when the limit
flag is 0). Values are kept to the ranges the Justification and Hyphenation
options take, and a spacing triple out of order goes back to its default.

Operands of the legacy type attributes are read by their place: an attribute
whose operands are missing, are not numbers, or are flags other than 0 and 1
stays as it was. Before, a value that was not a number shifted the later
operands (`5 /x 7 Ti` set indents of 5, 7 and 0), and a bad `TA` or `Ta`
turned kerning on or set the alignment to left. One long `Tx` no longer goes
past the reader's limit on characters.
2026-11-02 12:03:33 +07:00
dotCooCoo 06d037c1b2 .ai import: a clipping path keeps its stroke
A clipping path painted with a stroke (`W S`, `W B`) lost the stroke when
a .ai file opened from its editing data. The clipping path now keeps it,
and it paints over the clipped art as a clipping path's stroke does in
VectorCraft. A gradient-filled clipping path stroked with `1 BB` or `2 BB`
put the stroke on the painted copy of its fill, under the clipped art; it
now goes on the clipping path too. Several clipping paths in one group
make one compound path, which keeps their stroke, opacity and blend mode
when they all have the same ones.
2026-11-02 12:03:31 +07:00
dotCooCoo fa8d3b8de4 Fix #3797: documents open in Preview, and Outline is a setting of each document
View > Outline was one app-wide flag saved in the interface preferences,
so after Cmd+Y every later launch drew every opened or new document as
outlines, with no fills. The flag is now part of each document's view:
a document opens in Preview, each tab shows its own Outline or Preview,
and the setting is not saved at launch. An older preferences file that
holds the flag still loads, and its value is ignored.

The Outline command and ui.set's outline act on the active document and
are an error without one.
2026-11-02 12:03:30 +07:00
Brandon Thomas b676c626e0 Merge pull request #5186 from dotCooCoo/fix/legacy-ai-character-rotation
Legacy .ai: type with rotated characters is written as outlines that keep the rotation
2026-11-01 23:45:34 -05:00
Brandon Thomas 9d2d633bc3 Merge pull request #5184 from dotCooCoo/fix/eps-spot-reserved-colorant-names
EPS: spot inks named All or None print as spot inks
2026-11-01 23:45:30 -05:00
Brandon Thomas d3ef9a6aae Merge pull request #5183 from dotCooCoo/fix/replace-symbol-clears-overrides
Symbols: Replace Symbol clears the old symbol's dynamic overrides
2026-11-01 23:45:26 -05:00
Brandon ThomasandClaude Opus 5.5 bce3de36ca Legacy .ai: docs say when the rotated-type warning shows, and record the change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 23:27:52 -05:00
dotCooCoo 880a078053 Legacy .ai: type with rotated characters is written as outlines that keep the rotation
A version 7 text object has no arbitrary-angle character rotation: its
character attributes include baseline shift (Ts), scaling (Tz) and
tracking (Tt), Tv only turns Roman characters 90 degrees in vertical
text, and the positioning operators such as Tm are ignored on reading.
Type with Character Rotation was written as editable text with the
rotation left out, so rotated characters opened upright.

Such type is now written as its glyphs' outlines, the way type 8-bit text
can't hold already is, with the warning "type with rotated characters is
written as outlines, which keep the rotation, and is no longer editable
text". The outlines keep each character's rotation, baseline shift and
scale. Type without rotated characters, and type turned a whole number of
times, is still written as text.

Docs: docs/file-format-parity.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-01 16:35:49 -08:00
dotCooCoo 7984606471 EPS: spot inks named All or None print as spot inks
A Separation space whose colorant is named All paints every plate, and
one named None paints nothing (ISO 32000-1:2008, 8.6.6.4). EPS export
wrote a spot swatch's name as its colorant, so a spot color named All
printed on every plate and one named None did not print.

Such an ink is now written as All Spot or None Spot, numbered All Spot 2
and on when a spot swatch already has that name, in its Separation
space, DeviceN spaces and the %%DocumentCustomColors and
%%CMYKCustomColor comments. At PostScript Level 3, a gradient that mixes
one of these inks with another spot ink or with process or gray stops is
now a DeviceN shading instead of process colors. Registration still
writes All.
2026-11-01 16:32:41 -08:00
dotCooCoo 589c56da22 PDF export: a white stop linked to a process swatch keeps a spot gradient on its ink
A gradient of one spot ink's tints and paper white is written as a
Separation shading in that ink, with each white stop at 0% of it. A
white stop linked to a swatch that is not a spot color (a global White
swatch, for example) did not count as paper white. In an RGB document
the spot ink was then dropped: a Classic gradient was written in the
stops' own colors, and a Perceptual one was exported as an image in
process colors.

Such a stop now counts as paper white in the Separation and DeviceN
paths and in the checks that pick them, as an unlinked white stop does.
A stop linked to a white spot color is still a stop of that ink.
2026-11-01 16:28:40 -08:00
dotCooCoo 2adf5a9a61 Symbols: Replace Symbol clears the old symbol's dynamic overrides
An override on a dynamic symbol instance names an object of its symbol's
art by path. Replace Symbol kept every override whose path the new
symbol's art also had, so a change made to one object of the old symbol
was applied to an unrelated object of the new one. An instance given a
different symbol now drops its overrides; an instance replaced with its
own symbol keeps them. Undo brings them back.
2026-11-01 16:24:33 -08:00
Brandon Thomas 639c9d6ce9 Merge pull request #5113 from dotCooCoo/feat/recolor-color-sliders
Recolor Artwork: color sliders in a chosen color mode edit the selected row's new color
2026-11-01 15:28:03 -05:00
Brandon Thomas fceaf44fd0 Merge pull request #5138 from dotCooCoo/fix/vars-p6
Variables: a text value on threaded type replaces the whole story
2026-11-01 15:27:58 -05:00
Brandon ThomasandClaude Opus 5.5 872effcf77 Recolor: docs say a map row's model can't be null either
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 15:12:13 -05:00
dotCooCoo be20929877 Variables: a text value on threaded type replaces the whole story
Applying a data set wrote a text value into the bound frame only, and the
thread's reflow then joined it with the other frames' old text. The art no
longer matched the row it had just applied, a captured row applied back lost
text, and a value bound to a later frame deleted the rest of the story.

- A text value bound to any frame of threaded text replaces the whole story,
  which then flows through the frames again. `overflow` names the story's
  last frame.
- Capture Data Set, Update Data Set and the Variables panel's match check
  read such a frame as the whole story, frames joined in thread order.
- When any frame of the story is locked, the text value is skipped and
  counted in `skipped`, and the story is not read.
- Two text variables bound in one story apply in definition order, as for
  two on one object: the one defined last wins.

Docs: docs/mcp.md.
2026-11-01 08:58:35 -08:00
dotCooCoo f1fd255120 .ai import: version 7 palettes keep their swatches without names
Version 7 files write each palette swatch as its paint and `Pc`, with no
name. The reader only kept a swatch with a name before `Pc`, so such a
palette gave no swatches and the document got the default set.

A swatch without a name is now named by its spot color, gradient or
pattern, or by its values in the format the swatch import already uses
for unnamed colors ("Gray K=25", "C=25 M=0 Y=0 K=0"). `Pn` and a `Pc`
with no paint before it add nothing. Named swatches keep their names.

An operator the reader does not know, or a `Pg`, now ends the paint
before it, so a later `(name) Pc` no longer takes that paint. The
swatch limit is kept with a running count instead of summing every
group at each `Pc`.
2026-11-01 06:55:53 -08:00
dotCooCoo 9b853004d5 Recolor Artwork: a slider edit changes the color mode of its own row only
The color sliders now mark the row they edit with `model: "new"`
instead of a dialog-wide flag, and `recolor.apply` reads a row's own
`model` before the command's. Rows the sliders did not edit keep each
color's model, so with RGB art and a CMYK color group, a slider edit of
one row no longer turns the other rows' art CMYK. Another edit of the
row's new color, Reset and rows reduced again remove the mark, and
`recolor.randomize` moves it with its new color.
2026-11-01 02:06:46 -08:00
dotCooCoo f242b9caa6 Recolor Artwork: a row's tint stays with its spot color
A wheel drag or a harmony rule on the Edit tab now removes a row's tint
when it gives the row another new color, as the color sliders, the hex
field and the Color Picker already do. With Limit to Library, an edit
that snaps back to the row's own color keeps the tint.

recolor.randomize moves each row's tint with its new color when it
shuffles the order, and removes the tint from a color that saturation
and brightness changes.
2026-11-01 01:55:46 -07:00
dotCooCoo 46cb088eae Recolor Artwork: color sliders edit the selected row's new color in a chosen mode
The Assign tab gets a Color Mode menu (HSB, RGB, CMYK, Lab, and Tint when
the row's new color is a spot color of the Limit to Library library) and
its sliders under the rows. They edit the first selected row's new color
and are disabled with no row selected. Once they have edited a color in a
mode, the preview and OK send recolor.apply with model "new", so the art
takes the color in that mode.

recolor.apply rows take an optional tint (percent, default 100): art linked
to the row's global or spot library swatch takes that share of its tint.
recolor.randomize keeps a row's tint.
2026-10-31 21:42:42 -07:00
bflatastic a76d9215da Fix #4923: Find Nearest Swatch matches colours to a library's swatches
Object > Plug-ins > Color Effects > Find Nearest Swatch... picks a
library (the document's swatches, a built-in library, or a library or
colour book the user saved or loaded) and Fill and Stroke, and lists
each colour of the selection with its three closest swatches and their
CIEDE2000 difference. Apply gives each colour its nearest swatch as one
undo step.

- addon.color.nearestSwatch (a query): the closest swatches of a
  library to each colour of the selection's fills and strokes (solid
  colours and gradient stops, inside groups and type), or to a colour
  given, with deltaE; count 1..50.
- addon.color.applyNearestSwatch: recolours those paints (and gradient
  meshes) to their nearest swatch; global and spot swatches link, and a
  library's swatches the colours take are added to the document first
  (as swatch.library.add adds them), so a spot colour stays spot.
- The library picker Image Trace's Document Library uses moves to the
  Swatches panel module and is shared.
- No colour book data ships: only libraries VectorCraft computes or the
  user loads are searched.
2026-11-01 11:09:11 +07:00
dotCooCoo 38bb213917 PDF export: a Perceptual gradient that mixes spot and process inks keeps its inks
A Perceptual linear or radial gradient that mixes spot colors with each
other or with CMYK or gray stops was written as an image in process
colors. It is now written as a DeviceN shading of its inks, with the
stops on its Perceptual curve: at each one, the inks of the two stops on
either side are mixed at the weight where the two colors mixed in
display RGB have the OKLab lightness of the color on the curve.

- Gradient::ink_mixes gives the curve's stops as weights between stops,
  under the same 4,096-stop limit as ink_tints.
- Perceptual gradients that mix a spot color with RGB or Lab stops,
  dithered fills and freeform gradients stay images, and the warning
  now names those cases.
2026-11-01 11:09:09 +07:00
dotCooCoo 7e5e099f8e Export for Screens: a second file with a taken name is numbered instead of skipped
Squashed from #5028 onto main.
2026-11-01 11:09:08 +07:00
dotCooCoo ff036557fb Symbols: Edit Symbol opens the symbol in its own editing mode, and every instance follows each edit
Squashed from #5064 onto main.
2026-11-01 11:09:03 +07:00
dotCooCoo 5716a996ab Type: Touch Type moves a character rotated near 90 degrees across its line
Baseline shift now moves a character in the line's frame, outside
Character Rotation, so a positive shift moves a character up from its
line at any rotation, and the character turns about its shifted pen
position. The Touch Type tool splits a drag into its part along the line
(kerning) and its part up from the line (baseline shift) at every
rotation; before, within about 6 degrees of a quarter turn it dropped
the cross-line part.

Characters that already combine a rotation with a baseline shift are
drawn in a new position. Their stored values do not change.

- text: glyph transform order, PositionedGlyph::pivot (the turning
  point), tab leaders in the same order
- tools: Touch Type splits drags against the line's up axis and measures
  rotate-handle turns about the shifted pen position
- engine: text.touchType description; dy is up from the line
- docs: mcp.md Touch Type paragraph, target-app-parity.md progress line
2026-11-01 11:08:57 +07:00
dotCooCoo 94ef9b87c2 Type: Character panel toggles show off for a mixed selection, and text.setFormat checks on/off values
The Character panel's All Caps, Underline and Strikethrough buttons, and
its menu's All Caps, Underline, Strikethrough, Proportional Metrics,
Tate-chu-yoko and No Break, read the first selected character's style.
With only some characters marked they could show on, and choosing one
turned the attribute off for every character. They now show on only
when every selected character has the attribute, and a click on a mixed
selection turns it on for all, as Small Caps and Standard Vertical Roman
Alignment do. type.underline (Cmd+Shift+U) underlines a mixed selection
the same way. The Character Alignment and Digits submenus check no item
while the selected characters differ.

text.setFormat ignored a value that was not true or false for underline,
strikethrough, allCaps, proportionalMetrics, tateChuYoko,
standardVerticalRoman, noBreak, hyphenate, hyphenateCapitalized and
romanHangingPunctuation. It now refuses it with "`<key>` must be true or
false" and changes nothing, as text.setRangeStyle does.
2026-11-01 11:08:56 +07:00
dotCooCoo 427bce12b0 Character: Font Size can measure the cap height, x-height or ICF box
Squashed from #5026 onto main.
2026-11-01 11:08:55 +07:00
dotCooCoo af133b1ee4 .ai import: envelopes open as live envelopes
An envelope in a .ai's editing data opened as its distorted art, with
its mesh and contents dropped. It now opens as a live envelope of its
contents: a warp with its style, bend, distortions and orientation, a
mesh with its points and handles, or a top object with its outline,
with Envelope Options, Fidelity and Edit Contents.

The envelope replaces its drawn art only when VectorCraft draws it like
that art, with a budget so that live envelopes don't make the file open
from its PDF part. When the layers still differ too much from the page,
the drawn art is put back. An envelope that can't be read, or that
draws differently, keeps its drawn art with a note. Envelopes of type
keep their drawn art and hidden type as before.
2026-11-01 11:08:49 +07:00
dotCooCoo c027947362 .ai import: Objects on Path groups open live with their path, Pivot and Rotate
Squashed from #5059 onto main.
2026-11-01 11:08:47 +07:00
dotCooCoo 70651f585e .ai import: template layers and ruler guides open as they were set
The editing data's layer options set a template layer from the flag four
places after the colour index. The colour index of nine-operand options,
and the colour index and dimming percentage of eleven and twelve operand
options, are read from their own places (they were read one place later).

A straight (N) * guide path directly on a layer that runs across the
whole canvas, or from edge to edge of one artboard, opens as a ruler
guide on that layer (an artboard guide of that artboard); other guide
paths stay guide paths. Guides scale with the document when its editing
data is in the units of a UserUnit page, and an EPS that keeps only its
first artboard keeps only that artboard's guides.

Fixes #779
2026-11-01 11:08:18 +07:00
bflatastic 9b9f0918f0 Fix #4687: the Artboards panel reorders artboards by dragging a row 2026-11-01 10:17:29 +07:00
Don Omondi 1e8ac2434c Fix #4920: Document Tools exports every open document
addon.document.exportAllOpen {folder, format?} writes a copy of every
open document to one folder as Save a Copy does, each named after its
document with taken names numbered so no file is replaced. Documents
keep their paths and modified state, the active one stays active, and a
document that can't be written is reported while the others are still
written. Object > Plug-ins > Document Tools gets Export All Open
Documents as PDF and as SVG, which ask for the folder.
2026-11-01 09:24:25 +07:00
dotCooCoo 18b9c6113e .ai import: one object the reader can't read comes in as the page's art, and the layers stay
Squashed from #5050 onto main.
2026-11-01 09:24:23 +07:00
dotCooCoo b253356b4d Knife: a straight cut's ends snap to the anchors and paths Smart Guides snap to
Squashed from #5044 onto main.
2026-11-01 09:24:13 +07:00
bflatastic 84336967aa Fix #4922: Expand Dashes into Paths 2026-11-01 09:08:07 +07:00
bflatastic 317423c514 Fix #4908: Glyphs panel highlights the selected character 2026-11-01 09:08:04 +07:00
dotCooCoo d72022daf5 Grid tools: F, V, X and C change the divider skews by 10 while a grid is dragged out
Squashed from #5019 onto main.
2026-11-01 08:33:02 +07:00
dotCooCoo f07b9ab765 Live Paint: Live Paint Bucket Options set what a click paints and the highlight
Double-clicking the Live Paint Bucket did nothing, and the tool always
filled on a click, stroked on Shift-click and highlighted in red at
4 px. It now opens Live Paint Bucket Options with Paint Fills, Paint
Strokes, Highlight, Color and Width.

- Paint Fills (on) and Paint Strokes (off) set what a click paints.
  With Paint Strokes alone, a click strokes the edge within 4 px of the
  pointer. With both on, a click strokes an edge within 4 px and
  otherwise fills the face. With both off, a click paints nothing.
- Shift switches Paint Fills alone to Paint Strokes alone and back, and
  switches both to Paint Fills alone. With the defaults, Shift-click
  strokes the edge as before.
- Highlight turns the outline of the face or edge under the pointer on
  or off; Color (a layer color or a custom color) and Width (1 to 20 px)
  set it. With Paint Strokes alone, a selection of ordinary paths no
  longer highlights a region, since a click there paints nothing.
- The options are tool options (tool.setOption {tool:
  "livePaintBucket"}): they last across tool switches and are saved
  with the preferences.

Docs: docs/mcp.md, docs/target-app-parity.md.
2026-11-01 08:32:57 +07:00
dotCooCoo ece78f052f Type: Standard Vertical Roman Alignment stands half-width characters upright in vertical type
Character panel menu > Standard Vertical Roman Alignment, with Show East
Asian Options, is now a check item. It sets a new character attribute,
`standardVerticalRoman` (text.setFormat, text.setRangeStyle), off by
default and saved only when on. In vertical type the marked Latin
letters, digits and other characters that would lie on their side stand
upright one below the other, each in its upright cell. Spaces keep their
width and tate-chu-yoko blocks stay across the column. Horizontal type
keeps the mark and does not change. A combining mark shaped as its own
glyph turns with its letter, in Latin and CJK upright type alike.

Character Style Options labels the Character Alignment row "Character
Alignment" and gives Standard Vertical Roman Alignment its own checkbox.
Reset Panel turns the attribute off. PDF and SVG export place the glyphs
from the layout and need no change.
2026-11-01 08:32:55 +07:00
dotCooCoo ac896cfe3d PDF export: a Perceptual gradient of one spot ink's tints keeps its ink
A Perceptual linear or radial gradient was always written as an image,
so a gradient of spot ink tints lost its plate, and no warning named the
spot ink.

PDF export now writes a Perceptual gradient whose stops are tints of one
spot color (and paper white) as a Separation shading in that ink. Its
tints follow the gradient's curve: at each stop the renderer places on
the curve, the tint with the OKLab lightness of the color there.

Dithered fills, freeform gradients and Perceptual gradients that mix a
spot color with other colors are still images, and the export now warns
when such a gradient has stops linked to a spot swatch that its spot
colors are written in process colors.
2026-11-01 08:32:54 +07:00
bflatastic cd277d5d84 Fix #4917: Sort Objects sorts the selection by one attribute and applies that order to another
Squashed from #5017 onto main.
2026-11-01 08:11:17 +07:00
bflatastic ef80709e16 Fix #4925: Randomize Opacity gives each selected object a random opacity in a range 2026-11-01 08:07:33 +07:00
bflatastic 7d0c0b589e docs: type-parity keeps one status line and one revision row for the Serbian and Croatian languages after the rebase 2026-11-01 07:38:03 +07:00
VladimirCericandClaude Opus 5.5 13dd4f9086 Languages: keep other apps' names out of the comments and docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wCJ9DkszhZKxYL3LgcSfZ
2026-11-01 07:38:03 +07:00
VladimirCericandClaude Opus 5.5 d440b6d32b Languages: cite the quote sources (Pravopis ISBN, CLDR links)
From the CodeRabbit review: the Serbian and Croatian quote marks name
their sources with the Pravopis's ISBN and links to CLDR's sr and hr
data, in the code and in docs/type-parity.md; the quotes test says what
it checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wCJ9DkszhZKxYL3LgcSfZ
2026-11-01 07:38:03 +07:00
VladimirCericandClaude Opus 5.5 c492d524c5 Languages: Serbian (Cyrillic, Latin) and Croatian, with their quote marks
Document Setup and Character › Language list Serbian: Cyrillic (sr-Cyrl),
Serbian: Latin (sr-Latn) and Croatian (hr). Text tagged Serbian is shaped
in it (#4940, #4959), so fonts draw their Serbian forms, the italic б,
г, д, п, т. Serbian quotes follow the Pravopis (Matica srpska 2010,
т. 168) and CLDR: „…” and ’…’; Croatian per CLDR: „…“ and ‚…‘.
Illustrator lists Croatian but not Serbian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wCJ9DkszhZKxYL3LgcSfZ
2026-11-01 07:38:03 +07:00
Don Omondi 5b639169cd Fix #4918: Symbols panel Select All Unused
symbol.unused lists the symbols nothing places: instances in the art
(opacity masks included), graph designs, pattern tiles and brush art
count, and so do instances in a used symbol's art. The Symbols panel
menu's Select All Unused shows them selected, and Delete Symbol asks
once and deletes those still unused in one undo step
(symbol.delete {names}).
2026-11-01 07:38:01 +07:00
dotCooCoo 7410b7233e i18n: link, font and export status messages use real plural forms instead of "(s)"
The status bar's messages for relinked, replaced, updated and embedded
images, broken links, linked files that can't be read or changed on
disk, images that can't be decoded, added and unavailable fonts, copied
CSS, downloads and exports with notes read "1 image(s)" and similar.
They now say "1 image" and "2 images", and every catalog translates
both through @msgplural rows in the language's plural forms, as the
Package status does.

- i18n::msg_n writes such a message in English and fills its
  placeholders in one pass; the status bar's translation fills them in
  one pass too, so a path or name that holds "{n}" text stays whole.
- The old "(s)" @msg rows are replaced in all 26 catalogs, with new
  rows for Replaced and Break Link.
- Sites: dialogs/missing_links.rs, dialogs/missing_fonts.rs,
  app/links.rs, panels/css_properties.rs, dialogs/find_links.rs,
  panels/links.rs, place.rs, canvas.rs, io/export.rs and
  dialogs/save_for_web.rs.
2026-11-01 07:16:32 +07:00
dotCooCoo 13628274b7 OpenType: the panel turns Stylistic and Titling Alternates on and off, and offers Proportional Lining figures
The OpenType panel gains Stylistic Alternates (salt) and Titling Alternates
(titl) checkboxes after Swash. The Figure menu lists Default Figure, Tabular
Lining, Proportional Lining, Proportional Oldstyle and Tabular Oldstyle.
Proportional Lining sets pnum and lnum and turns tnum and onum off; Tabular
Lining keeps lnum, Proportional Oldstyle keeps pnum, and Default Figure and
Tabular Oldstyle turn both off. Every catalog has rows for the three new
labels.
2026-11-01 07:16:31 +07:00
dotCooCoo 8805fa8214 Place: Word .doc text with a broken surrogate pair is refused, and fields without a result warn
A 16-bit piece holding half of a UTF-16 surrogate pair was placed with
U+FFFD in its place; the document is now refused. A pair split between
two pieces is decoded as one character.

A field with no result (no separator, never ended, or nested past 64)
is left out with its own warning, and the warning about fields placed
as their results is given only for fields that have one.
2026-11-01 07:16:30 +07:00