0

[PDF Ink Signatures] Add getAnnotationBrush message handler to backend

Add a getAnnotationBrush message handler in the PDF plugin, so that the
PDF extension can send this message and avoid storing all brush data.

This CL only sets up the message handler. It is not yet being used by
the PDF extension.

Bug: 373672165
Change-Id: I9b28489f45b2ebe6191fb2a171186c3015e28474
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5941005
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Andy Phan <andyphan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1372446}
This commit is contained in:
Andy Phan
2024-10-23 01:11:21 +00:00
committed by Chromium LUCI CQ
parent 93eefaccfa
commit 1cedaf471c
8 changed files with 226 additions and 3 deletions

@ -101,6 +101,17 @@ std::optional<PdfInkBrush::Type> PdfInkBrush::StringToType(
return std::nullopt;
}
// static
std::string PdfInkBrush::TypeToString(Type brush_type) {
switch (brush_type) {
case Type::kHighlighter:
return "highlighter";
case Type::kPen:
return "pen";
}
NOTREACHED();
}
// static
bool PdfInkBrush::IsToolSizeInRange(float size) {
return size >= 1 && size <= 16;

@ -44,6 +44,8 @@ class PdfInkBrush {
// does not correspond to any `Type`.
static std::optional<Type> StringToType(const std::string& brush_type);
static std::string TypeToString(Type brush_type);
// Returns whether `size` is in range or not.
static bool IsToolSizeInRange(float size);

@ -20,11 +20,13 @@
#include "base/containers/fixed_flat_map.h"
#include "base/feature_list.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/ranges/algorithm.h"
#include "base/time/time.h"
#include "base/values.h"
#include "pdf/draw_utils/page_boundary_intersect.h"
#include "pdf/input_utils.h"
#include "pdf/message_util.h"
#include "pdf/pdf_features.h"
#include "pdf/pdf_ink_brush.h"
#include "pdf/pdf_ink_conversions.h"
@ -227,6 +229,8 @@ bool PdfInkModule::OnMessage(const base::Value::Dict& message) {
base::MakeFixedFlatMap<std::string_view, MessageHandler>({
{"annotationRedo", &PdfInkModule::HandleAnnotationRedoMessage},
{"annotationUndo", &PdfInkModule::HandleAnnotationUndoMessage},
{"getAnnotationBrush",
&PdfInkModule::HandleGetAnnotationBrushMessage},
{"setAnnotationBrush",
&PdfInkModule::HandleSetAnnotationBrushMessage},
{"setAnnotationMode", &PdfInkModule::HandleSetAnnotationModeMessage},
@ -599,6 +603,53 @@ void PdfInkModule::HandleAnnotationUndoMessage(
ApplyUndoRedoCommands(undo_redo_model_.Undo());
}
void PdfInkModule::HandleGetAnnotationBrushMessage(
const base::Value::Dict& message) {
CHECK(enabled_);
base::Value::Dict reply = PrepareReplyMessage(message);
// Get the brush type from `message` or the current brush type if not
// provided.
const std::string* brush_type_message = message.FindString("brushType");
std::string brush_type_string;
if (brush_type_message) {
brush_type_string = *brush_type_message;
} else {
brush_type_string =
is_drawing_stroke()
? PdfInkBrush::TypeToString(drawing_stroke_state().brush_type)
: "eraser";
}
base::Value::Dict data;
data.Set("type", brush_type_string);
if (brush_type_string == "eraser") {
data.Set("size", eraser_size_);
reply.Set("data", std::move(data));
client_->PostMessage(std::move(reply));
return;
}
std::optional<PdfInkBrush::Type> brush_type =
PdfInkBrush::StringToType(brush_type_string);
CHECK(brush_type.has_value());
const ink::Brush& ink_brush = GetBrush(brush_type.value()).ink_brush();
data.Set("size", ink_brush.GetSize());
SkColor color = GetSkColorFromInkBrush(ink_brush);
base::Value::Dict color_reply;
color_reply.Set("r", static_cast<int>(SkColorGetR(color)));
color_reply.Set("g", static_cast<int>(SkColorGetG(color)));
color_reply.Set("b", static_cast<int>(SkColorGetB(color)));
data.Set("color", std::move(color_reply));
reply.Set("data", std::move(data));
client_->PostMessage(std::move(reply));
}
void PdfInkModule::HandleSetAnnotationBrushMessage(
const base::Value::Dict& message) {
CHECK(enabled_);
@ -658,7 +709,11 @@ PdfInkBrush& PdfInkModule::GetDrawingBrush() {
const PdfInkBrush& PdfInkModule::GetDrawingBrush() const {
CHECK(is_drawing_stroke());
switch (drawing_stroke_state().brush_type) {
return GetBrush(drawing_stroke_state().brush_type);
}
const PdfInkBrush& PdfInkModule::GetBrush(PdfInkBrush::Type brush_type) const {
switch (brush_type) {
case (PdfInkBrush::Type::kHighlighter):
return highlighter_brush_;
case (PdfInkBrush::Type::kPen):

@ -243,6 +243,7 @@ class PdfInkModule {
void HandleAnnotationRedoMessage(const base::Value::Dict& message);
void HandleAnnotationUndoMessage(const base::Value::Dict& message);
void HandleGetAnnotationBrushMessage(const base::Value::Dict& message);
void HandleSetAnnotationBrushMessage(const base::Value::Dict& message);
void HandleSetAnnotationModeMessage(const base::Value::Dict& message);
@ -269,6 +270,9 @@ class PdfInkModule {
PdfInkBrush& GetDrawingBrush();
const PdfInkBrush& GetDrawingBrush() const;
// Returns the brush with type `brush_type`.
const PdfInkBrush& GetBrush(PdfInkBrush::Type brush_type) const;
// Converts `current_tool_state_` into segments of `ink::InProgressStroke`.
// Requires `current_tool_state_` to hold a `DrawingStrokeState`. If there is
// no `DrawingStrokeState`, or the state currently has no inputs, then the

@ -52,6 +52,9 @@ class PdfInkModuleClient {
// Notifies the client whether annotation mode is enabled or not.
virtual void OnAnnotationModeToggled(bool enable) {}
// Asks the client to post `message`.
virtual void PostMessage(base::Value::Dict message) {}
// Notifies the client that a stroke has finished drawing or erasing.
virtual void StrokeFinished() {}

@ -17,6 +17,7 @@
#include "base/files/file_path.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "pdf/pdf_features.h"
#include "pdf/pdf_ink_brush.h"
@ -153,6 +154,17 @@ std::map<int, std::vector<raw_ref<const ink::Stroke>>> CollectVisibleStrokes(
return visible_stroke_shapes;
}
base::Value::Dict CreateGetAnnotationBrushMessageForTesting(
const std::string& brush_type) {
base::Value::Dict message;
message.Set("type", "getAnnotationBrush");
message.Set("messageId", "foo");
if (!brush_type.empty()) {
message.Set("brushType", brush_type);
}
return message;
}
// Optional parameters that the `setAnnotationBrushMessage` may have, depending
// on the brush type.
struct TestAnnotationBrushMessageParams {
@ -212,6 +224,8 @@ class FakeClient : public PdfInkModuleClient {
return base::Contains(visible_page_indices_, page_index);
}
MOCK_METHOD(void, PostMessage, (base::Value::Dict message), (override));
void StrokeFinished() override { ++stroke_finished_count_; }
MOCK_METHOD(void, UpdateInkCursorImage, (SkBitmap bitmap), (override));
@ -301,6 +315,137 @@ TEST_F(PdfInkModuleTest, UnknownMessage) {
EXPECT_FALSE(ink_module().OnMessage(message));
}
// Verify that a get eraser message gets the eraser parameters.
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageEraser) {
EnableAnnotationMode();
EXPECT_TRUE(ink_module().enabled());
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "eraser",
"size": 3.0,
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(ink_module().OnMessage(
CreateGetAnnotationBrushMessageForTesting("eraser")));
}
// Verify that a get pen message gets the pen brush parameters.
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessagePen) {
EnableAnnotationMode();
EXPECT_TRUE(ink_module().enabled());
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "pen",
"size": 3.0,
"color": {
"r": 0,
"g": 0,
"b": 0,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(
ink_module().OnMessage(CreateGetAnnotationBrushMessageForTesting("pen")));
}
// Verify that a get highlighter message gets the highlighter brush parameters.
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageHighlighter) {
EnableAnnotationMode();
EXPECT_TRUE(ink_module().enabled());
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "highlighter",
"size": 8.0,
"color": {
"r": 242,
"g": 139,
"b": 130,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(ink_module().OnMessage(
CreateGetAnnotationBrushMessageForTesting("highlighter")));
}
// Verify that a get brush message without a parameter gets the default brush
// parameters.
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageDefault) {
EnableAnnotationMode();
EXPECT_TRUE(ink_module().enabled());
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "pen",
"size": 3.0,
"color": {
"r": 0,
"g": 0,
"b": 0,
},
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(
ink_module().OnMessage(CreateGetAnnotationBrushMessageForTesting("")));
}
// Verify that a get brush message without a parameter gets the current brush
// parameters if the brush has changed from the default brush.
TEST_F(PdfInkModuleTest, HandleGetAnnotationBrushMessageCurrent) {
EnableAnnotationMode();
EXPECT_TRUE(ink_module().enabled());
// Set the brush to eraser.
EXPECT_TRUE(ink_module().OnMessage(CreateSetAnnotationBrushMessageForTesting(
"eraser", /*size=*/4.5, nullptr)));
EXPECT_CALL(client(), PostMessage)
.WillOnce([](const base::Value::Dict& dict) {
auto expected = base::test::ParseJsonDict(R"({
"type": "getAnnotationBrushReply",
"messageId": "foo",
"data": {
"type": "eraser",
"size": 4.5,
},
})");
EXPECT_THAT(dict, base::test::DictionaryHasValues(expected));
});
EXPECT_TRUE(
ink_module().OnMessage(CreateGetAnnotationBrushMessageForTesting("")));
}
// Verify that a set eraser message sets the annotation brush to an eraser.
TEST_F(PdfInkModuleTest, HandleSetAnnotationBrushMessageEraser) {
EnableAnnotationMode();

@ -2066,14 +2066,16 @@ bool PdfViewWebPlugin::IsPageVisible(int page_index) {
return engine_->IsPageVisible(page_index);
}
#if BUILDFLAG(ENABLE_PDF_INK2)
void PdfViewWebPlugin::OnAnnotationModeToggled(bool enable) {
engine_->SetFormHighlight(/*enable_form=*/!enable);
if (enable) {
engine_->ClearTextSelection();
}
}
#endif // BUILDFLAG(ENABLE_PDF_INK2)
void PdfViewWebPlugin::PostMessage(base::Value::Dict message) {
client_->PostMessage(std::move(message));
}
void PdfViewWebPlugin::StrokeFinished() {
base::Value::Dict message;

@ -437,6 +437,7 @@ class PdfViewWebPlugin final : public PDFiumEngineClient,
float GetZoom() const override;
bool IsPageVisible(int page_index) override;
void OnAnnotationModeToggled(bool enable) override;
void PostMessage(base::Value::Dict message) override;
void StrokeFinished() override;
void UpdateInkCursorImage(SkBitmap bitmap) override;
void UpdateThumbnail(int page_index) override;