mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-02 14:38:13 -06:00
A stamp placed on a page with /Rotate was drawn in user space, so the label or picture turned with the page. Counterrotate the appearance so it reads upright as the page is displayed. Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
co-authored by
Brandon Thomas
parent
dfa43beb9f
commit
fff985f30d
@@ -264,6 +264,11 @@ pub(crate) fn picture_turn(d: &Dict) -> i64 {
|
||||
d.get(b"PCPictureRotate").and_then(Object::as_int).unwrap_or(0).rem_euclid(360) / 90 * 90
|
||||
}
|
||||
|
||||
/// The page `/Rotate` a rubber stamp's label is turned back by (`/PCStampRotate`).
|
||||
fn stamp_turn(d: &Dict) -> i64 {
|
||||
d.get(b"PCStampRotate").and_then(Object::as_int).unwrap_or(0).rem_euclid(360) / 90 * 90
|
||||
}
|
||||
|
||||
/// The first text [`build`] would draw in the standard fonts (Helvetica, WinAnsiEncoding) that
|
||||
/// they can't, which would come out as `?`: what it is (for the message) and the character. The
|
||||
/// text of a text box, callout or Fill & Sign text, a measurement's label, a dynamic stamp's
|
||||
@@ -632,7 +637,26 @@ pub fn build(d: &Dict) -> Option<Stream> {
|
||||
}
|
||||
let kind = crate::StampKind::from_name(other)?;
|
||||
let by = d.get(b"PCByLine").and_then(|o| o.as_string()).map(PdfString::to_text);
|
||||
return Some(stamp(kind, rect, stroke.unwrap_or(kind.color()), by.as_deref(), opacity, res));
|
||||
let turn = stamp_turn(d);
|
||||
let draw = if turn == 0 {
|
||||
rect
|
||||
} else {
|
||||
let (rw, rh) = (rect[2] - rect[0], rect[3] - rect[1]);
|
||||
let (dw, dh) = if matches!(turn, 90 | 270) { (rh, rw) } else { (rw, rh) };
|
||||
if dw < 1.0 || dh < 1.0 {
|
||||
return None;
|
||||
}
|
||||
[0.0, 0.0, dw, dh]
|
||||
};
|
||||
let mut stream = stamp(kind, draw, stroke.unwrap_or(kind.color()), by.as_deref(), opacity, res);
|
||||
if turn != 0 {
|
||||
// Upright in the stamp's displayed box, then mapped into `/Rect` the way
|
||||
// the page maps display space into user space. Viewers fit that box to
|
||||
// `/Rect`; the page rotation then shows the label the right way up.
|
||||
let m = pdfcraft_model::view_matrix_for(turn, rect);
|
||||
stream.dict.set(b"Matrix".to_vec(), Object::Array(m.iter().copied().map(Object::Real).collect()));
|
||||
}
|
||||
return Some(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -1091,6 +1091,11 @@ pub fn add_annotation(doc: &mut Document, new: &NewAnnotation, meta: &Meta) -> R
|
||||
if let Some(b) = by {
|
||||
d.set(b"PCByLine".to_vec(), PdfString::text(b));
|
||||
}
|
||||
// The label is drawn for the page as displayed. A later page rotation is a new edit.
|
||||
let turn = pdfcraft_model::pages(doc).get(new.page).map_or(0, |p| p.rotation(doc));
|
||||
if matches!(turn, 90 | 180 | 270) {
|
||||
d.set(b"PCStampRotate".to_vec(), Object::Int(turn));
|
||||
}
|
||||
}
|
||||
Shape::TypedSignature { contours, .. } => {
|
||||
d.set(b"C".to_vec(), rgb(style.color));
|
||||
@@ -1237,8 +1242,9 @@ pub fn add_annotation(doc: &mut Document, new: &NewAnnotation, meta: &Meta) -> R
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
/// Counterrotate a newly placed image stamp's appearance into displayed-page axes.
|
||||
/// The normal appearance's transformed bounding box is fitted to `/Rect` by PDF viewers.
|
||||
/// Counterrotate a newly placed picture stamp's appearance into displayed-page axes.
|
||||
/// The picture may be an image or a PDF page. The normal appearance's transformed bounding
|
||||
/// box is fitted to `/Rect` by PDF viewers.
|
||||
pub fn orient_image_stamp(doc: &mut Document, page: usize, index: usize, rotation: i64) -> Result<(), AnnotError> {
|
||||
let matrix = match rotation {
|
||||
90 => [0.0, 1.0, -1.0, 0.0, 0.0, 0.0],
|
||||
@@ -1248,8 +1254,8 @@ pub fn orient_image_stamp(doc: &mut Document, page: usize, index: usize, rotatio
|
||||
};
|
||||
let (_, r) = annot_ref(doc, page, index)?;
|
||||
let d = annot_dict(doc, r);
|
||||
if d.name(b"Subtype") != Some(b"Stamp") || !matches!(d.get(b"PCPictureImage"), Some(Object::Bool(true))) {
|
||||
return Err(AnnotError::Invalid("only an image stamp can be oriented".into()));
|
||||
if d.name(b"Subtype") != Some(b"Stamp") || d.get(b"PCPicture").is_none() {
|
||||
return Err(AnnotError::Invalid("only a picture stamp can be oriented".into()));
|
||||
}
|
||||
let normal = d
|
||||
.get(b"AP")
|
||||
@@ -1316,8 +1322,8 @@ fn draw_appearance(doc: &mut Document, r: ObjRef) -> Result<(), AnnotError> {
|
||||
/// Pure from the dictionary and the objects it points at, so [`AppearanceCache`] can reuse it.
|
||||
fn build_appearance(doc: &Document, d: &Dict) -> Option<Stream> {
|
||||
let mut stream = appearance::build(d)?;
|
||||
// Image stamp restyling must retain the placement's page-axis correction.
|
||||
if matches!(d.get(b"PCPictureImage"), Some(Object::Bool(true))) {
|
||||
// Picture-stamp restyling must retain the placement's page-axis correction.
|
||||
if d.get(b"PCPicture").is_some() {
|
||||
let matrix = d.get(b"AP").and_then(|ap| {
|
||||
let ap = doc.resolve(ap);
|
||||
let normal = doc.resolve(ap.as_dict()?.get(b"N")?);
|
||||
|
||||
Reference in New Issue
Block a user